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你的价值进入我 。
* 作为旁注: *除了资源清理之外,永远不要编写任何业务逻辑。 这会导致可读性并导致错误。