try-catch-finally 和 return 的执行顺序是怎样的?

try-catch-finally 和 return 的执行顺序是怎样的?

首页休闲益智throw it out更新时间:2024-07-14

最近一直在看Java虚拟机规范,发现直接分析bytecode更能加深对Java语言的理解。

之前看过一篇关于 return 和 finally 执行顺序的文章,仅在 Java 的语言层面做了分析,其实我倒觉得直接看 bytecode 可能来得更清晰一点。

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

try - finally

publicclassExceptionTest{ publicvoidtryFinally(){ try{ tryItOut(); }finally{ wrapItUp(); } } //auxiliarymethods publicvoidtryItOut(){} publicvoidwrapItUp(){} }

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

publicvoidtryFinally(); Code: 0:aload_0 1:invokevirtual#2//MethodtryItOut:()V 4:aload_0 5:invokevirtual#3//MethodwrapItUp:()V 8:goto18 11:astore_1 12:aload_0 13:invokevirtual#3//MethodwrapItUp:()V 16:aload_1 17:athrow 18:return Exceptiontable: fromtotargettype 0411any

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

0:aload_0 1:invokevirtual#2//MethodtryItOut:()V 4:aload_0 5:invokevirtual#3//MethodwrapItUp:()V 18:return

如果抛出了异常,JVM 会在

Exceptiontable: fromtotargettype 0411any

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

11:astore_1 12:aload_0 13:invokevirtual#3 16:aload_1 17:athrow

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

12:aload_0 13:invokevirtual#3

最后通过

16:aload_1 17:athrow

重新抛出异常。

通过以上分析可以得出结论:

在try-finally中,try块中抛出的异常会首先保存在local variable中,然后执行finally块,执行完毕后重新抛出异常。

如果我们把代码修改一下,在try块中直接return。

try - return - finally

publicvoidtryFinally(){ try{ tryItOut(); return; }finally{ wrapItUp(); } }

”反汇编“一下:

0:aload_0 1:invokevirtual#2//MethodtryItOut:()V 4:aload_0 5:invokevirtual#3//MethodwrapItUp:()V 8:return 9:astore_1 10:aload_0 11:invokevirtual#3//MethodwrapItUp:()V 14:aload_1 15: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块

publicvoidtryCatchFinally(){ try{ tryItOut(); }catch(TestExce){ handleExc(e); }finally{ wrapItUp(); } }

javap一下

publicvoidtryCatchFinally(); Code: 0:aload_0 1:invokevirtual#2 4:aload_0 5:invokevirtual#3 8:goto31 11:astore_1 12:aload_0 13:aload_1 14:invokevirtual#5 17:aload_0 18:invokevirtual#3 21:goto31 24:astore_2 25:aload_0 26:invokevirtual#3 29:aload_2 30:athrow 31:return Exceptiontable: fromtotargettype 0411ClassTestExc 0424any 111724any

通过Exception table可以看出:

也就说 catch block 本身也在 finally block 的管辖范围之内。

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

来源:http://liangfei.me/

查看全文
大家还看了
也许喜欢
更多游戏

Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved