JVM如何执行Try catch finally块

根据Java语言规范, 第14.20.2节

通过首先执行try块来执行具有finally块的try语句。 然后有一个选择:

  • 如果try块的执行正常完成,则执行finally块,然后有一个选择:
    • 如果finally块正常完成,则try语句正常完成。
    • 如果finally块因为原因S而突然完成,则try语句因为S而突然完成

如果我正确地解释它然后在执行try块finally之后调用,但是这一切是如何工作的以及为什么我得到了输出,

public static int TestTryFinallyBlock() { int i =0; try { i= 10; //Perform some more operation return i; } finally { i = 40; } } public static void main( String[] args ) { int i1 = TestTryFinallyBlock(); //Here the output was 10 not 40 } 

我想知道这个东西是如何产生输出10的。

这是当执行try块并且遇到return语句时,输出值已经被推送到堆栈,然后执行finally块

我知道首先遇到返回然后最后块运行所以输出是10,但jvm如何解释或者jvm如何处理或转换try finally块?
是jvm使用GOTO部分跳转部分到最后部分还是堆栈已经维护?

经过一番搜索并看到生成了什么字节码后,我发现实际上看起来没有最终的块,也没有JVM生成的跳转或goto语句。
上面的代码翻译为(如果我正确解释字节代码,如果我错了请请纠正我)

 public static int TestTryFinallyBlock() { int returnValue; //A temporary return variable try { int i = 0; i = 10; returnValue = i; i = 40; return returnValue; } catch (RuntimeException e) { i = 40; //finally section code id copied here too throw e; } } 

注意:如果'i'是对可变类对象的引用,并且在finally块中更改了对象的内容,那么这些更改也会反映在返回的值中。

最后编译

try-finally语句的编译类似于try-catch的编译。 在try语句之外传递控制之前,无论该传输是正常还是突然,因为抛出了exception,必须首先执行finally子句。 对于这个简单的例子:

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

编译后的代码是:

 Method void tryFinally() 0 aload_0 // Beginning of try block 1 invokevirtual #6 // Method Example.tryItOut()V 4 jsr 14 // Call finally block 7 return // End of try block 8 astore_1 // Beginning of handler for any throw 9 jsr 14 // Call finally block 12 aload_1 // Push thrown value 13 athrow // ...and rethrow value to the invoker 14 astore_2 // Beginning of finally block 15 aload_0 // Push this 16 invokevirtual #5 // Method Example.wrapItUp()V 19 ret 2 // Return from finally block Exception table: From To Target Type 0 4 8 any 

控制传递到try语句之外有四种方法:通过返回该块的底部,返回,执行break或continue语句,或者引发exception。

要了解更多有关javac如何解释finally块的信息。请参阅JLS – 3.13。 最后编译

当您输入返回值时,该方法已准备好返回10. 10在堆栈上作为返回值。 执行finally块并将i设置为40 – 但是i与返回值的位置不同。 现在,如果有副作用,如:

 public static int TestTryFinallyBlock() { int i =0; try { i= 10; //Perform some more operation return i; } finally { i = 40; System.out.println("local: "+i); } } 

打印40。

这是因为返回语句函数的方式以及它与使用finally语句的try进行交互的方式。

JLS的第14.17节描述了退货声明。

带有Expression的return语句尝试将控制权转移给包含它的方法的调用者; Expression的值成为方法调用的值。 更准确地说,执行这样的return语句首先评估Expression。 如果表达式的评估由于某种原因突然完成,则返回语句因此而突然完成。 如果表达式的评估正常完成,产生值V,则return语句突然完成,原因是返回值为V.

最后一句表示如果正常计算 return语句的表达式,则return语句将完全删除 。 在您的示例中, try块由于return语句而导致abrutply终止,并且原因是return值10( i计算为值10)。

因为在你的例子中, return是在finally块的try中,JLS的第14.20.2节告诉我们接下来会发生什么:

  • 如果try块的执行由于任何其他原因R突然完成,则执行finally块,然后有一个选择:
    • 如果finally块正常完成,则try语句突然完成,原因是R.
    • 如果finally块因为原因S而突然完成,那么try语句因为原因S(以及原因R被丢弃)而完全崩溃。

因此,由于返回语句被评估为值10,并且因为finally块正常完成,因此try块突然终止,然后该方法返回10。

现在i的值是40,但是你没有得到40 ,因为你在得到40之前得到( returned )了这个值。

就像是

 i=10 return i i=40 if return here,you get 40 

虽然不是一个好习惯,但仅限于演示。

 public static int TestTryFinallyBlock() { int i =0; try { i= 10; //Perform some more operation } finally { i = 40; return i; } } 

现在40你的价值进入

* 作为旁注: *除了资源清理之外,永远不要编写任何业务逻辑。 这会导致可读性并导致错误。