起源:http://liangfei.me/

最近始终在看Java虚拟机标准,发现间接剖析bytecode更能加深对Java语言的了解。

之前看过一篇对于 returnfinally 执行程序的文章,仅在 Java 的语言层面做了剖析,其实我倒感觉间接看 bytecode 可能来的更清晰一点。

先看一个只有 try-finally,没有 catch 的例子。

try - finally

public class ExceptionTest {  public void tryFinally() {    try {      tryItOut();    } finally {      wrapItUp();    }  }  // auxiliary methods  public void tryItOut() { }  public void wrapItUp() {}}

通过 javap -c ExceptionTest 来查看它的字节码。

public void tryFinally();  Code:     0: aload_0     1: invokevirtual #2  // Method tryItOut:()V     4: aload_0     5: invokevirtual #3  // Method wrapItUp:()V     8: goto          18    11: astore_1    12: aload_0    13: invokevirtual #3  // Method wrapItUp:()V    16: aload_1    17: athrow    18: return  Exception table:     from    to  target type         0     4    11   any

如果没有抛出异样,那么它的执行程序为

0: aload_01: invokevirtual #2  // Method tryItOut:()V4: aload_05: invokevirtual #3  // Method wrapItUp:()V18: return

如果抛出了异样,JVM 会在

Exception table:   from    to  target type       0     4    11   any

中进行管制跳转。如果是位于0到4字节之间的命令抛出了任何类型(any type)的异样,会跳转到11字节处持续运行。

11: astore_112: aload_013: invokevirtual #316: aload_117: athrow

astore_1会把抛出的异样对象保留到local variable数组的第二个元素。上面两行指令用来调用成员办法wrapItUp。

12: aload_013: invokevirtual #3

最初通过

16: aload_117: athrow

从新抛出异样。

通过以上剖析能够得出结论:

在try-finally中,try块中抛出的异样会首先保留在local variable中,而后执行finally块,执行结束后从新抛出异样。

如果咱们把代码批改一下,在try块中间接return。

try - return - finally

public void tryFinally() {  try {    tryItOut();    return;  } finally {    wrapItUp();  }}

”反汇编“一下:

 0: aload_0 1: invokevirtual #2 // Method tryItOut:()V 4: aload_0 5: invokevirtual #3 // Method wrapItUp:()V 8: return 9: astore_110: aload_011: invokevirtual #3 // Method wrapItUp:()V14: aload_115: athrow

能够看出finally块的代码依然被放到了return之前。

如果try块中有return statement,肯定是finally中的代码先执行,而后return。

JVM标准是这么说的:

Compilation of a try-finally statement is similar to that of try-catch. Pior to transferring control outside thetry statement, whether that transfer is normal or abrupt, because an exception has been thrown, thefinally clause must first be execute.
try - catch - finally

给下面的代码加一个catch块

public void tryCatchFinally() {  try {    tryItOut();  } catch (TestExc e) {    handleExc(e);  } finally {    wrapItUp();  }}

javap一下

public void tryCatchFinally();  Code:     0: aload_0     1: invokevirtual #2     4: aload_0     5: invokevirtual #3     8: goto          31    11: astore_1    12: aload_0    13: aload_1    14: invokevirtual #5                      17: aload_0    18: invokevirtual #3    21: goto          31    24: astore_2    25: aload_0    26: invokevirtual #3    29: aload_2    30: athrow    31: returnException table:   from    to  target type       0     4    11   Class TestExc       0     4    24   any      11    17    24   any

通过Exception table能够看出:

  • catch监听 0 ~ 4 字节类型为TextExc的异样。
  • finally为 0 ~ 4 以及 11 ~ 17 字节任何类型的异样。

也就说 catch block 自身也在 finally block 的管辖范畴之内。

如果catch block 中有 return statement,那么也肯定是在 finally block 之后执行。

近期热文举荐:

1.600+ 道 Java面试题及答案整顿(2021最新版)

2.终于靠开源我的项目弄到 IntelliJ IDEA 激活码了,真香!

3.阿里 Mock 工具正式开源,干掉市面上所有 Mock 工具!

4.Spring Cloud 2020.0.0 正式公布,全新颠覆性版本!

5.《Java开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞+转发哦!