15.1 异常处理机制
使用try...catch捕获异常:如果执行try块里的业务逻辑代码出现异常,则系统自动创建一个异常对象,该异常会被提交给Java运行时环境,这个过程被称为抛出(throw)异常。当Java运行时环境收到异常时,会寻找能处理该异常对象的catch块,如果找到合适的catch块,就把这个异常交给catch块处理,这个过程被称为捕获(catch)异常;如果Java运行时环境找不到捕获异常的catch块,则运行时环境终止,Java程序也将退出。
异常类的继承体系:Java把所有的非正常情况称为两种,异常Exception和错误Error,都继承了Throwable父类。Error错误无法恢复和捕获,将导致应用程序中断。
Java7提供的多异常捕获:捕获多种类型的异常时,多种异常类型之间使用竖线|隔开;异常变量有隐式的final修饰,因此程序不能对异常变量重新赋值。
访问异常信息的方法(通过catch块的后异常形参进行调用):
- getMessage():返回该异常的详细描述字符串。
- printStackTrace():将该异常的跟踪栈信息输出到标准错误输出。
- printStackTrace(PrintStream s):将该异常的跟踪栈信息输出到指定输出流。
- getStackTrace():返回该异常的跟踪栈信息。
使用finally回收资源:不管try块中的代码是否出现异常,也不管哪一个catch块被执行,甚至在try块或catch块中执行了return语句,finally块总会被执行,用于回收物理资源。
块的必要性与顺序:异常处理语法结构中只有try块是必须的,即没有try块,就不能有后面的catch块和finally块;catch块和finally块至少出现其中之一,也可以同时出现;可以有多个catch块,捕获父类异常catch块必须位于捕获子类异常的后面,否则将出现编译错误;不能只有try块时,既没有catch块,也没有finally块;多个catch块位于try块之后,finally块位于所有的catch块之后。
块中同时包含结束语句:try块和catch块里的return语句和throw语句都会某方法结束,但是程序会去寻找异常处理流程中是否包含finally块,如果没有则立刻执行return或throw语句,方法执行终止;如果有finally块,系统立即开始执行finally块,finally块执行完毕后,return或throw语句才会执行。如果finally块里也是用了return或throw语句,则finally块会终止该方法,try块和catch块里的语句将不会执行。
Java7自动关闭资源的try语句:在try关键字后紧跟一对圆括号,圆括号里可以声明、初始化一个或多个需要显式关闭的资源,try语句会在执行结束后自动关闭这些资源。这些资源的实现类必须实现了AutoCloseable或Closeable接口的close()方法(AutoCloseable接口的close方法声明抛出Exception异常,Closeable接口的close方法声明抛出IOException异常)。自动关闭资源的try块相当于包含了隐式的finally块,因此try语句可以既没有catch块,也没有finally块。
15.2 Checked异常与Runtime异常
Java程序必须显式处理Checked异常,要么用使用try...catch块捕获该异常,要么抛出该异常;Runtime异常无须显式声明抛出,当然也可以使用try...catch块捕获该异常。
使用throws声明抛出异常:当前方法不知道如何处理该异常时,可以将该异常抛出给上一级调用者处理;如果main方法无法处理该异常,可以使用throws声明抛出给Java虚拟机。如果调用的某个方法,throws了Checked异常,则调用者必须采取行动。而使用Runtime异常时,程序无须在方法中声明抛出异常。子类方法声明抛出的异常类型应该是父类方法声明抛出的异常类型的子类或相同,子类方法声明抛出的异常不能比父类方法声明抛出的异常数量多。
使用throw抛出异常:使用throw抛出一个异常实例,系统同样会终止程序执行查找可以捕获异常的catch块。如果抛出的异常是Checked异常,则必须进行处理。如果是Runtime异常,既可以不作处理,也可以try...catch处理或throws抛出。通过catch和throw联合使用,可以让多个方法协同处理同一个异常。
自定义异常类:继承Exception类或者RuntimeException类,实现无参数构造方法和带一个字符串消息参数的方法。
Java7增强的throw语句:如果抛出一个NumberFormatException异常,catch捕获时的异常新参是Exception类型,如果想在catch里throw出这个异常并且在方法签名上throws,在Java7以前只能throws Exception类型;Java7以后,编译器会检查throw异常的具体类型,所以可以throws NumberFormatException类型的异常。
异常链:将捕获的异常包装进一个新的异常,并重新抛出异常的方式。JDK1.4以后,所有的Throwable子类在构造器中都可以接受一个原始异常作为参数。使用异常链之后,可以追踪到异常最初发生的地方。
Java的异常跟踪栈:异常对象的printStackTrace()方法用于打印异常的跟踪栈信息。跟踪栈记录所有的异常发生点等具体信息。
15.3 异常处理规则
- 不要过度使用异常,不要使用异常处理来代替正常的业务处理逻辑判断。
- 不要使用过于庞大的try块,不便于区分异常类型,应该对try块进行分解。
- 避免使用catch All语句捕获所有异常。
- 不要忽略捕获到的异常,应输出日志或进行处理。