Java中的异常处理是处理运行时错误的强大机制之一,从而可以保持应用程序的正常流程。
问题:
- checked exception和unchecked exception的区别是什么?
- data=50/0 这句代码背后发生了什么?
- 为什么使用多个catch块?
- 是否存在finally块未被执行的可能?
- 什么是异常传播?
- JVM如何处理异常
- 为什么异常比较费时?
- 为什么在程序终止之前都能执行finally代码块?
Java异常类的继承关系图
检查异常和非检查异常的区别:
- 检查异常:除了RuntimeException和Error之外,直接继承Throwable类的类称为检查异常,例如IOException、SQLException等。
- 非检查异常:继承RuntimeException的类被称为未检查的异常,例如ArithmeticException、NullPointerException、ArrayIndexOutOfBoundsException等。未检查的异常在编译时不被检查,但在运行时进行检查。Error也可以被划分为非检查异常,因为它也是在运行时进行检查
- 错误:错误是不可恢复的,例如 OutOfMemoryError,VirtualMachineError,AssertionError等
java异常处理的内部工作?
JVM首先检查异常是否被处理。如果未处理异常,JVM提供执行下列任务的默认异常处理程序:
- 打印异常描述。
- 打印堆栈跟踪(异常发生的方法的层次结构,由栈顶到栈低)。
- 终止程序。
但是,如果应用程序程序员处理了异常,则保持应用程序的正常流程,即执行其余代码。
多个catch块使用规则
- 在一个时间只有一个异常发生,并且在一个时间只有一个catch块被执行
- 所有catch块必须从最特定到最通用的顺序排列,即ArithmeticException的catch必须先于Exception的.
什么情况下不会执行finally代码块?
如果程序退出(可能调用System.ext()或导致进程中止的致命错误(Error)),则不会执行最后一个块。常见的在try或catch块中return都会执行finally块
什么是异常传播?
首先从堆栈的顶部抛出异常,如果未捕获到异常,则将调用堆栈下拉到前一个方法;如果没有捕获到异常,则将异常再次下拉到前一个方法,以此类推,直到捕获到异常或到达调用堆栈的最底部。称为异常传播。
异常传播规则:
- 默认情况下,非检查异常(运行时检查的的异常)在调用链中被转发(传播)。
- 默认情况下,检查异常(编译时检查的异常)不会在调用链中转发(传播)。
JVM如何处理异常?
在编译生成的字节码中,每个方法都附带一个异常表。异常表中的每一个条目代表一个异常处理器,并且由 from 指针、to 指针、target 指针以及所捕获的异常类型构成。这些指针的值是字节码索引(bytecode index,bci),用以定位字节码。其中,from 指针和 to 指针标示了该异常处理器所监控的范围,例如 try 代码块所覆盖的范围。target 指针则指向异常处理器的起始位置,例如 catch 代码块的起始位置。
为什么异常比较耗时?
当异常发生时,首先根据抛出异常所在的位置和异常类型去当前方法的异常表中匹配合适的条目,如果存在匹配条目,程序计数器跳转到target所指的字节码所在的位置;否则向上层方法抛出异常,直到匹配得到能够处理该异常的栈帧,最差的情况就是遍历了当前线程的栈帧中的所有异常表
为什么在程序终止之前都能执行finally代码块?
虚拟机会复制finally块内的代码到try块和catch块的出口代码中
英文原文链接:https://www.javatpoint.com/exception-handling-in-java