异常允许我们(如果没有其他手段)强制程序停止运行,并告诉我们出现了什么问题,或者(理想状态下)强制程序处理问题,并返回到稳定状态。
用 new
在堆上创建异常对象,这也伴随着存储空间的分配和构造器的调用,所有标准异常类都有两个构造器:一个是默认构造器,另一个是接受字符串作为参数,以便能把相关信息放入异常对象的构造器:1
throw new NullPointerException("t = null");
此外,能够抛出任意类型的 Throwable
对象,它是异常类型的根类。
创建自定义异常
1 | class AException extends Exception { |
printStackTrace()
方法将打印:从方法调用处直到异常抛出处的方法调用序列,默认是将信息输出到标准错误流。printStackTrace(System.out)
代表将信息发送到 System.out
。
异常与记录日志
1 | class AException extends Exception { |
栈轨迹
printStackTrace()
方法所提供的信息可以通过 getStackTrace()
方法来直接访问,这个方法将返回一个由栈轨迹中的元素所构成的数组,其中每一个元素都表示栈中的一帧。数组中的第一个元素是栈顶元素,并且是调用序列中最后一个方法调用。数组中最后一个元素是栈底元素,是调用序列中第一个方法调用。
1 | class A { |
重新抛出异常
重抛异常会把异常抛给上一级环境中的异常处理程序,同一个 try
块的后续 catch
子句将被忽略。此外,异常对象的信息将得以保存,除非使用 fillInStackTrace()
更新异常信息。
1 | class A { |
异常链
在捕获一个异常后抛出另一个异常,并且希望能把原始异常的信息保存下来:
Error
,Exception
,RuntimeException
提供了带参数的构造器。- 其他类型的异常,通过
initCause()
方法实现
1 | class A extends Exception { |
Java 标准异常
Throwable
被用来表示任何可以作为异常被抛出的类,其对象可以分成两类:
Error
,用来表示编译时错误和系统错误Exception
,是可以被抛出的基本类型
RuntimeException
类型的异常,也被称作 不受检查异常
,这种异常不需要编译器强制进行检查。如果 RuntimeException
没有被捕获而直达 main()
,那么在程序退出前将自动调用异常的 printStackTrace()
方法。
异常丢失
1 | try { |
finally
1 | class A { |
1 | class A { |
异常限制
当覆盖方法时,只能抛出在基类方法的异常说明里列出的那些异常。
异常说明本身并不属于方法类型的一部分,方法类型是由方法的名字与参数的类型组成的。因此,不能基于异常说明来重载方法。此外,一个出现在基类方法的异常说明中的异常,不一定会出现在派生类方法的异常说明里。
1 | class AException extends Exception { |
构造器
涉及到构造器时,尽量不要在 finally
里面进行清理。因为很可能构造器在执行过程中就抛出异常,导致部分对象没有被成功创建。 尽量使用 try-catch
子句嵌套。
1 | class InputFile { |
异常使用指南
应该在下列情况下使用异常:
- 在恰当的级别处理问题(在知道该如何处理的情况下,才去捕获异常)
- 解决问题并且重新调用产生异常的方法
- 进行少许修补,然后绕过异常发生的地方继续执行
- 用别的数据进行计算,以代替方法预计会返回的值
- 把当前运行环境下能够做的事情尽量做完,然后把相同的(或者不同的)异常抛到更高层
- 终止程序
- 进行简化
- 让类库和程序更安全