《Java开发手册》学习进程之第12章异常处理
程序在运行中若遇到异常,在没有try-catch语句处理时,程序会停止运行,即发生异常的后面的代码将不会执行,并抛出异常。
在try-catch语句中,try语句块中的某处发生异常时,try语句块里剩下的代码将不会执行,但通过catch语句块捕获异常后,后面的代码将继续执行。
如果没有catch语句块捕获异常,异常将沿着方法的调用栈一直向上传播。如果传播的过程中一直没有catch语句块捕获异常,则最终传播到main方法,最后由main方法抛出异常,由JRE来处理。
finally语句块在执行完try-catch语句前能保证执行,但也有可能因为一些特殊原因无法执行:
- finally语句块里本身产生异常。
- 执行finally语句块的线程死亡。
- finally语句块执行了“System.exit(0);”语句。
- 最后那就是计算机断电了*_*;
捕获异常:
Java类库中有一个java.lang.Throwable类,其继承自java.lang.Object类,是所有异常类的超类。
注意书中(特别是《Java核心技术卷1》)给出的各个异常类的层次结构:
- Throwable类有两个直接子类,Error类与Exception类,Exception类有一个子类RuntimeException。
- Java语言规范将派生于Error类或RuntimeException类的所有异常称为未捕获异常(不用必须处理的异常,又称unchecked,即未检查异常,),其他的都为捕获异常(必须处理的异常,又称checked,即已检查异常)。
捕获异常一般是由外界导致的,并且是可以恢复的。而这些异常,即使程序本身没有问题,也有可能会发生,所以在开发时必须考虑如何处理。
Java中规定,在调用可能抛出捕获异常的方法时,必须编写处理异常的代码,否则编译不通过。
若try语句块里不可能抛出某种类型的捕获异常,但在catch里又编写了该捕获异常的处理代码,这时,将不能通过编译。(但对未捕获异常就没有这种限制)
未捕获异常:
继承自Error的类一般代表由硬件运行失败导致的严重错误,这种异常一发生,程序是不能恢复的。
RuntimeException类的子类通常是指一些程序运行时错误引起的异常。
未捕获异常可以通过编译,但如果一发生,程序运行时就会出现错误,抛出异常。
再次抛出异常:
在实际开发中,不可能所有的异常在其产生的位置都能立即处理,有时需要上报——异常的再次抛出,由上一级程序进行处理。这与异常的传播类似(即没有被捕获,异常就会一层一层上报给调用者)。
一旦方法有可能抛出捕获异常,则在方法声明时需要特别指出,否则编译报错(由上述可知,在方法内部没有对有可能产生捕获异常的代码进行异常处理,编译也会报错)。
显性再抛出:通过编写代码将catch语句捕获的异常再次抛出。
注意12.4.2节关于显性再抛出的语法规则及在使用时需要注意的问题。
隐性再抛出:
- 若抛出的是捕获异常,隐性再招聘和显性再抛出都必须在方法声明中明确声明。
- 显性再抛出是方法体中使用throw语句将产生的异常抛出。
- 隐性再抛出是在方法体中没有任何抛出异常的语句,若产生异常将自动抛出。
构造器若可能产生异常,则要像上述使用方法一样使用构造器。
显性再抛出异常的作用:
一般我们直接将异常抛出时不需要显性再抛出这么麻烦,但有时并不是把收到的异常直接抛出,而是将收到的异常先进行处理,然后再抛出新的异常。可以降低程序中各模块的耦合性。
异常的匹配:
try-catch语句块中可以有任意多个catch语句块,但是这些catch语句块并不是随意放置的。为了异常匹配的需要,其顺序有严格的规定。
单个catch语句块中可以处理多种类型的异常:
- 指定处理的异常类型若没有任何子类,则只能捕获指定的异常类型。
- 指定处理的异常类型若有子类,则指定类型及其子类的异常都可以捕获。
多个catch语句的先后顺序:
若try后跟多个catch语句,并且各自指定的异常类型中,相互之间有派生关系,这时catch语句先后顺序就必须满足一定的规则,否则有可能编译失败。
若多个catch语句块中所指定的异常类型相互有派生关系,那么必须将子类型的异常写在上面,父类型的异常写在下面。
若为级别相同或者没有任何派生关系的异常类型,其catch语句放置的先后顺序无所谓。
注意12.8.3节关于断言的使用问题。
关于断言的滥用:
断言的作用是用来检查程序的逻辑正确性,如果使用断言不是为了这个目的那就是滥用断言。有一个简单的判断是否是滥用断言的方法,那就是如果在程序发布,正常运行时关闭断言会影响程序的性能,则一定是滥用断言。
- 断言表达式在使用前后程序的状态应该是一样的。例如,不可以在断言表达式中使用诸如“++”、“--”等能影响程序运行状态的运算。
- 断言失败是程序逻辑导致的,不应该对其进行捕获处理,而是应该改正程序逻辑上的问题。
- 断言检查只是在开发或测试阶段用到,不应该在发布后还需要用断言。