循序渐进学Java:异常
目录,更新ing,学习Java的点滴记录
目录放在这里太长了,附目录链接大家可以*选择查看--------Java学习目录
异常引入介绍
Java的基本理念是"结构不佳的代码不能运行"
- 在理想状态下,用户输入的数据的格式永远都是正确的,选择打开的文件也一定存在,并且永远不会出现bug.但是现实世界中却充满了不良的数据和带有问题的代码.
- 发现错误的理想时机是在编译阶段,也就是在你试图运行程序之前.然而,编译期间并不能找出所有的错误,余下的问题必须在运行期间解决.这就需要错误源能通过某种方式,把适当的信息传递给某个接收者—该接收者将知道如何正确处理这个问题
- Java使用
异常
来提供一致的错误报告模型,使得构件能够与客户端代码可靠地沟通问题.Java中的异常处理的目的在于通过使用少于目前数量的代码来简化大型可靠的程序的生成
. - 假设一个Java程序运行qi9jian出现了一个错误.这个错误可能是由于文件包含了错误信息,或者网络连接出现问题造成的.也有可能是因为使用无效的数组下标,或者视图使用一个没有被赋值的对象引用而造成的.用户期望在出现错误时,程序能够采用一些理智的行为.如果由于出现错误而使得某些操作没有完成,程序应该:
1)返回到一种安全状态,并能够让用户执行一些其他命令
2)允许用户保存操作的结果,并以妥善的方式终结程序. - 案例—如果我们要拷贝一个文件,在没有异常机制的情况下,我们需要考虑各种异常情况,伪代码如下:
以上方式的坏处
1) 逻辑代码和错误代码放在一起
2) 程序员本身需要考虑的例外情况较复杂,对编程人员要求高
使用Java的异常机制提供了方便的处理方式
异常机制本质:当程序出现异常时,程序安全的退出,处理完后继续执行的机制
异常机制概念
- 异常指程序运行过程中出现的非正常现象,例如用户输入错误、除数为零、需要处理的 文件不存在、数组下标越界等。
- 在 Java 的异常处理机制中,引进了很多用来描述和处理异常的类,称为异常类。异常类定义中包含了该类异常的信息和对异常进行处理的方法。
所谓异常处理,就是指程序在出现问题时依然可以正确的执行完。
- 案例:------分析异常机制工作流程
运行结果显示,发生了ArithmeticException异常(算术运算异常),原因在于除数为0,并且程序在此处中断,并没有执行后面打印"222"的代码
现在使用try-catch来处理,发现程序遇到异常可以正常处理,处理完成后,程序继续往下执行,IDEA快速生成try-catch的方式是,选中代码后,按ctrl+alt+t,然后选择try-catch
程序在执行“1/0”仍然遇到异常,然后进行 try-catch 处理。处理完毕后,程序继续往下执行,打印了“222”内容。 -
Java 是采用面向对象的方式来处理异常的。处理过程:
抛出异常
:在执行一个方法时,如果发生异常,则这个方法生成代表该异常的一个对象
,停止当前执行路径,并把异常对象提交给 JRE。
捕获异常
:JRE 得到该异常后,寻找相应的代码来处理该异常。JRE 在方法的调用栈中查找,从生成异常的方法开始回溯,直到找到相应的异常处理代码为止。 -
使用异常的技巧
(可以等看完全部异常内容后再分析)
1) 异常处理不能代替简单的测试—只有在异常情况下使用异常机制
比如对一个栈进行退栈操作,一种选择是每次进行退栈操作前,首先查看栈是否为空----if(!s.empty()) s.pop();接下来,强行进行退栈操作.然后,捕获EmptyStackException异常来告知我们不能这样做.
在测试的机器上,前者运行时间远远小于后者
2) 不要过分地细化异常
不要尝试将每条语句都放入一个try语句块中,这会导致代码量急剧膨胀.
3) 利用异常层次结构
不要只抛出RuntimeException.应该寻找更加恰当的子类或者创建自己的异常类
不要只捕获Throwable异常,否则,程序代码更加难以理解,更难维护
4) 让高层的方法通知用户发生了错误
如果调用了一个抛出异常的方法,调用者就会本能地捕获这些可能的异常.其实,传递异常要比捕获异常更好
早抛出,晚捕获
异常分类
- Java中,异常对象都是派生于
Throwable类
的一个实例.如果Java内置的异常类不能够满足需求,用户可以创建自己的异常类 Java将派生与Error类或RuntimeException类的所有异常称为非受检查异常,所有其他的异常称为受检查异常.
- Java异常层次结构图
需要注意的是,所有的异常都是由Throwable继承而来,但在下一层就分解为两个分支:Error和Exception
- Error
Error类层次结构描述了Java运行时系统的内部错误和资源耗尽错误.应用程序不应该抛出这种类型的对象.如果出现了这样的内部错误,除了通告给用户,并尽力使程序安全地终止之外,再也无能为力了,这种情况很少出现 - Exception
Exception 是程序本身能够处理的异常,如:空指针异常(NullPointerException)、数 组 下 标 越 界 异 常 ( ArrayIndexOutOfBoundsException ) 、 类 型 转 换 异 常
(ClassCastException)、算术异常(ArithmeticException),格式化异常(NumberFormatException)等.
Exception 类是所有异常类的父类
,其子类对应了各种各样可能出现的异常事件。通常 Java 的异常可分为以下两块
1.RuntimeException 运行时异常(UncheckedException)
2.CheckedException 已检查异常 - 关于RuntimeException(运行时异常)
派生于 RuntimeException 的异常
,如被 0 除、数组下标越界、空指针等,其产生比较频繁,处理麻烦,如果显式的声明或捕获将会对程序可读性和运行效率影响很大。 因此由系统自动检测并将它们交给缺省的异常处理程序(用户可不必对其处理)。
这类异常通常是由编程错误导致的,所以在编写程序时,并不要求必须使用异常处理机制来处理这类异常,经常需要通过增加“逻辑处理来避免这些异常”。
"如果出现RuntimeException异常,那么一定是你的问题"
,这一一条相当有道理的规则 -
五个常见的RuntimeException
1) ArithmeticException 异常
解决方式
2) NullPointerException 异常
解决方案
3) ClassCastException 异常
解决方案
4) ArrayIndexOutOfBoundsException 异常
解决方案
5) NumberFormatException 异常
解决方案
CheckedException 已检查异常
- Exception派生结构下非RuntimeException外的异常,统称为Checked Exception,又被称为"已检查异常",如IOException、SQLException 等以及用户自定义的 Exception 异常。 这类异常在
编译时就必须做出处理,否则无法通过编译
。
异常处理方式_try_catch_finally捕获异常
- 捕获异常是通过 3 个关键词来实现的:
try-catch-finally
。用 try 来执行一段程序,如果出现异常,系统抛出一个异常,可以通过它的类型来捕捉(catch)并处理它,最后一步是通过 finally 语句为异常处理提供一个统一的出口,finally 所指定的代码都要被执行(catch 语句可有多条;finally 语句最多只能有一条,根据自己的需要可有可无)。 - 简单异常处理流程图
- try
try 语句指定了一段代码,该段代码就是异常捕获并处理的范围。在执行过程中,当任意一条语句产生异常时,就会跳过该条语句中后面的代码。代码中可能会产生并抛出一种或 几种类型的异常对象,它后面的 catch 语句要分别对这些异常做相应的处理。
一个 try 语句必须带有至少一个 catch 语句块或一个 finally 语句块
当异常处理的代码执行结束以后,不会回到 try 语句去执行尚未执行的代码。
- catch
每个 try 语句块可以伴随一个或多个 catch 语句,用于处理可能产生的不同类型的异常对象。
常用方法
toString ()方法,显示异常的类名和产生异常的原因
getMessage()方法,只显示产生异常的原因,但不显示类名
printStackTrace()方法,用来跟踪异常事件发生时堆栈的内容
catch 捕获异常时的捕获顺序
如果异常类之间有继承关系,在顺序安排上需注意。越是顶层的类,越放在下面,再不然就直接把多余的 catch 省略掉。 也就是先捕获子类异常再捕获父类异常 - finally
有些语句,不管是否发生了异常,都必须要执行,那么就可以把这样的语句放到 finally 语句块中
通常在 finally 中关闭程序块已打开的资源,比如:关闭文件流、释放数据库连接等
6.try-catch-finally 语句块的执行过程
程序首先执行可能发生异常的 try 语句块。如果 try 语句没有出现异常则执行完后跳至finally 语句块执行;如果 try 语句出现异常,则中断执行并根据发生的异常类型跳至相应的catch 语句块执行处理。catch 语句块可以有多个,分别捕获不同类型的异常。catch 语句块执行完后程序会继续执行 finally 语句块。finally 语句是可选的,如果有的话,则不管是否发生异常,finally 语句都会被执行。 - 注意
1) 即使 try 和 catch 块中存在 return 语句,finally 语句也会执行。是在执行完 finally 语句后再通过 return 退出。
2) finally 语句块只有一种情况是不会执行的, 那就是在执行 finally 之前遇到了System.exit(0)结束程序运行 - 示例—异常处理代码,
多个catch
可以捕获多种异常
异常处理方式_声明式异常处理(throws)
- 当 CheckedException 产生时,不一定立刻处理它,可以再把异常 throws 出去。
- 在方法中使用 try-catch-finally 是由这个方法来处理异常。但是在一些情况下,当前方法并不需要处理发生的异常,而是
向上传递给调用它的方法处理
。 - 如果一个方法中可能产生某种异常,但是并
不能确定如何处理这种异常
,则应根据异常规范在方法的首部声明该方法可能抛出的异常。 - 如果一个方法抛出多个已检查异常,就必须在方法的首部列出所有的异常,之间以
逗号
隔开。 - throws使用案例
- throw和throws
throws表示方法可能会抛出某个异常的声明,但并不一定会抛出异常
throw表示抛出一个异常
throw案例
throws声明异常,这里实际上并没有发生异常
JDK新特性_try-with-resource
- JAVA 中,JVM 的垃圾回收机制可以对内部资源实现自动回收,给开发者带来了极大的便利。但是
JVM 对外部资源(调用了底层操作系统的资源)的引用却无法自动回收
,例如数据库连接,网络连接以及输入输出 IO 流等。这些连接就需要我们手动去关闭
,不然会导致外部资源泄露,连接池溢出以及文件被异常占用等。
2.JDK7 之后, 新增了“ try-with-reasource”
。它可以自动关闭实现了AutoClosable 接口的类,实现类需要实现 close()方法。”try-with-resources 声明”,将 try-catch-finally 简化为 try-catch,这其实是一种语法糖,在编译时仍然会进行转化为 try-catch-finally 语句。 - 示例—如果需要开启多个资源,直接在try()括号内填写,并以分号间隔
自定义异常
- 不必拘泥于Java已有的异常类型.Java提供的异常体系不可能遇见所有的希望加以报告的错误,所以可以自己定义异常类来表示程序中可能会遇到的特定问题.
- 要自己定义异常类,必须从已有的异常类继承,最好是选择意思详尽的异常类继承(不过这样的异常并不容易找),因此,
我们可以定义一个派生于Exception的类,或者派生于Exception子类的类
. - 自定义异常类如果继承 Exception 类,则为受检查异常,必须对其进行处理;如果不想处理,可以让自定义异常类继承运行时异常 RuntimeException 类
- 习惯上,
自定义异常类应该包含 2 个构造器
:一个是默认的构造器,另一个是带有详细信息的构造器。 - 自定义异常类示例
IDEA调试功能_可视化bug追踪
- 断点breakpoint
程序运行到此处,暂时挂起,停止执行。我们可以详细在此时观察程序的运行情况,方 便做出进一步的判断。 - 设置断点
在行号后面单击即可增加断点
在断点上再单击即可取消断点 - 进入调试视图
我们通过如下方式都可以进入调试视图:
单击工具栏上的按钮:
右键单击编辑区,点击:debug ‘模块名’ 选项
- 调试视图布局
- 调试操作区功能