JVM如何捕获异常?
Java异常知识
1.异常的两大关键因素
(1)抛出异常
1.显式:应用程序手动抛出异常。具体就是使用throw抛出异常
2.隐式:Java虚拟机对于无法执行的代码,自动抛出异常
(2)捕获异常
1.try 代码块:用来标记需要进行异常监控的代码。
2.catch 代码块:跟在 try 代码块之后,用来捕获在 try 代码块中触发的某种指定类型的异常。除了声明所捕获异常的类型之外,catch 代码块还定义了针对该异常类型的异常处理器。在 Java中,try 代码块后面可以跟着多个 catch 代码块,来捕获不同类型的异常。Java 虚拟机会从上至下匹配异常处理器。因此,前面的 catch 代码块所捕获的异常类型不能覆盖后边的,否则编译器会报错。
3.fnally 代码块:跟在 try 代码块和 catch 代码块之后,用来声明一段必定运行的代码。它的设计初衷是为了避免跳过某些关键的清理代码,例如关闭已打开的系统资源。在程序正常执行的情况下,这段代码会在 try 代码块之后运行。否则,也就是 try 代码块触发异常的情况下,如果该异常没有被捕获,fnally 代码块会直接运行,并且在运行之后重新抛出该异常。如果该异常被 catch 代码块捕获,fnally 代码块则在 catch 代码块之后运行。在某些不幸的情况下,catch 代码块也触发了异常,那么 fnally 代码块同样会运行,并会抛出 catch 代码块触发的异常。在某些极端不幸的情况下,fnally 代码块也触发了异常,那么只好中断当前 fnally 代码块的执行,并往外抛异常。
2.异常的分类
1.所有异常的父类都是Throwable 2.Error异常是程序的执行状态无法恢复的状态,只能中止线程甚至中止JVM的异常 3.Exception是相对Error没有这么严重的异常 4.Runtime Exception和Error都属于不需要检查的异常 5.除了Runtime Exception和Error的异常都是Check Exception异常 6.Check Exception异常都是需要显式捕获的异常
3.Java虚拟机是如何捕获异常的?
java虚拟机构造异常实例非常昂贵。虚拟机需要生成该异常的栈轨迹。该操作会逐一访问当前线程的 Java 栈帧,并且记录下各种调试信息,包括栈帧所指向方法的名字,方法所在的类名、文件名,以及在代码中的第几行触发该异常。
既然异常实例的构造十分昂贵,我们是否可以缓存异常实例,在需要用到的时候直接抛出呢?从语法角度上来看,这是允许的。然而,该异常对应的栈轨迹并非 throw 语句的位置,而是新建异常的位置。
因此,这种做法可能会误导开发人员,使其定位到错误的位置。这也是为什么在实践中,我们往往选择抛出新建异常实例的原因。
异常处理器
1.来源:每个方法在编译的时候都会生成一个异常表。异常表里面的每一个条目都代表一个异常处理器。
2.组成:
(1)from指针,to指针:代表捕获异常的范围,就是Try的范围。
(2)target指针:代表处理器的开始位置,就是catch的起始位置。
(3)捕获的异常类型。
3.捕获异常
(1)当程序触发异常时,Java 虚拟机会从上至下遍历异常表中的所有条目。当触发异常的字节码的索引值在某个异常表条目的监控范围内,Java 虚拟机会判断所抛出的异常和该条目想要捕获的异常是否匹配。如果匹配,Java 虚拟机会将控制流转移至该条目 target 指针指向的字节码。
(2)如果遍历完所有异常表条目,Java 虚拟机仍未匹配到异常处理器,那么它会弹出当前方法对应的Java 栈帧,并且在调用者(caller)中重复上述操作。在最坏情况下,Java 虚拟机需要遍历当前线程 Java 栈上所有方法的异常表。
4.finally代码的编译:当前版本 Java 编译器的做法,是复制 fnally 代码块的内容,分别放在 try-catch 代码块所有正常执行路径以及异常执行路径的出口中。
代码1: Try{ Try block } catch { Catch block } finally { Finally block }
代码2: Try { Try block Finally block } catch { Catch block Finally block } finally{ Finally block }
代码1是我们的Java代码,代码2是编译之后的Java代码。注意:如果 catch 代码块捕获了异常,并且触发了另一个异常,那么 fnally 捕获并且重抛的异常是哪个呢?答案是后者。也就是说原本的异常便会被忽略掉,这对于代码调试来说十分不利。
5.Java7的 Supressed 异常以及语法糖
针对上节说的会将catch的异常忽略掉,Java7引入了 Supressed 异常处理这个问题。但是使用起来还是很麻烦(没有感受,