在开发中,try-catch、finaliy 的使用可以说是很常见了,那么它们转换成字节码之后又是什么样子呢?发生异常时字节码是如何执行的呢?今天我们就从字节码层面看看 try-catch 和 finaliy
首先我们先写一个demo,并转换成字节码,入下图👇,源码和字节码的对应关系我已经在图中框出来了,我们一个一个来分析
Exception table
如果一个方法里有 try-catch 代码块,那么在方法的字节码中就会出现一个Exception table
异常表。我们先看看异常表中几个字段的含义:
- from :字节码起始索引
- to :字节码结束索引(不包含)
- target :异常处理字节码索引起始位置
- type :异常类型(any表示任何类型)
从上图的字节码中可以看到,我们 try 代码块包裹的字节码是[0,11),所以异常表的前三行都是对 try 代码块中异常的捕获:
- 如果是NullPointerException,跳转到字节码22行(NullPointerException的catch代码块)
- 如果是NumberFormatException,跳转到字节码42行(NumberFormatException的catch代码块)
- 如果是其他类型异常,由于try-catch未捕获,跳转到62行,执行finaliy代码块,并在第72行athrow抛出异常
异常表的第四行和第五行是对 catch 代码块中的异常捕获,如果 catch 中也发生了异常,由于没有做任何处理,所以和第三行一样,也是跳转到62行,执行 finaliy 代码块,随后 athrow 抛出异常。
finaliy块的内联
从图中我们不难发现,finaliy 代码块(紫色框)在字节码中出现了4次,我们具体分析一下这四块:
- 如果没有发生异常,程序正常执行,走完
finaliy块1
后跳转73行,程序return
- 如果发生了NullPointerException,根据异常表会跳转到22行处理异常,然后执行
finaliy块2
后跳转73行,程序return
- 如果发生了NumberFormatException,根据异常表会跳转到42行处理异常,然后执行
finaliy块3
后跳转73行,程序return
- 如果发生了未捕获的异常,或者在catch代码块中发生了异常,根据异常表跳转到62行,执行
finaliy块4
后athrow抛出异常
综上所述,不管代码是否 catch 住了异常,都会走到 finaliy 代码块,这就保证了finaliy代码一定会被执行
总结
以上就是 try-catch、finaliy 在字节码层面的实现,其实并不复杂,核心逻辑是当异常发生时根据异常表去跳转到指定行数继续执行, finaliy 块通过内联的方式实现了finaliy代码一定会被执行的特性。