异常处理的靓噶组成要素是抛出异常和捕获异常。这两大要素共同实现程序控制流的非正常转移。
抛出异常可分为显示和隐式两种。显示抛出的主体是应用程序,它指的是在程序中使用throw关键字,手动将异常实例抛出。
隐式抛出异常的主体则是Java虚拟机,它指的是Java虚拟机在执行过程中,碰到无法执行的异常状态,自动抛出异常。
捕获异常则涉及如下三种代码块:
- try代码块:用来标记需要进行异常监控的代码。
- catch代码块:跟在try代码块之后,用来补货在try代码中触发的某种指定类型的异常。除了声明所捕获异常的类型之外,catch代码块还定义了针对该异常类型的异常处理器。try代码块后面可以跟多个catch代码块。
- finally代码块:跟在try代码块和catch代码块之后,用来声明一段必定运行的代码。
异常的基本概念
在Java语言规范中,所有的异常都是Throwable类或者子类的实例。Throwable有两大直接子类。第一个是Error,涵盖沉痼不应捕获的异常。当触发Error的时候,只能终止线程或者终止虚拟机。
第二个是Exception,涵盖程序可能需要捕获并处理的异常。
RunntimeException和Error属于Java里的非检查异常。其他异常则是属于检查异常。
异常实例的构造十分昂贵。在构造异常实例时,Java虚拟机便需要生成该异常的栈轨迹,该操作会逐一访问当前线程的Java栈帧,并且记录下各种调试信息,包括栈帧所指向方法的名字,方法所在的类名、文件名,以及在代码中第几行触发异常。
Java虚拟机是如何捕获异常的
在编译生成的字节码中,每个方法都附带一个异常表。异常表中的每一个条目代表一个异常处理器,并且由from指针、to指针、target指针以及所捕获的异常类型构成。这些指针的值是字节码索引,用于定位字节码。
其中,from指针和to指针标识了该异常处理器所监控的范围。target指针则执行异常处理器的起始位置。
当程序触发异常时,Java虚拟机会从上至下遍历异常表中的所有条目,进行匹配。如果匹配,Java虚拟机将控制流转移至该条目target指针指向的字节码。如果不匹配,在调用者中重复上述操作。
finally代码块的编译比较复杂。
Java 7的Suppressed异常以及语法糖
如果在catch中再次出现异常:需要使用Java 7 中的Suppressed异常来解决这个问题。
Java 7专门构造一个名为try-with-resources的语法糖,在字节码层面自动使用Suppressed异常。当然,该语法糖的主要目的并不是使用Suppressed异常,而是精简资源打开关闭的用法。
Java 7的try-with-resources语法糖,极大地简化了异常代码。程序可以在try关键字后声明并实例化实现AutoCloseable接口的类,编译器将自动添加对应的close()操作。在声明多个
AutoCloseable实例的情况下,编译生成的字节码类似于手动编写代码的编译结果。
Java 7还支持在同一catch代码块中捕获多种异常。
网友评论