JVM如何执行Try catch finally块

时间:2013-10-23 10:20:44

标签: java try-finally

根据Java语言规范,Section §14.20.2

  

首先执行try块,执行带有finally块的try语句。然后有一个选择:

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

如果我正确解释它,那么在执行try block 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部分跳转部分到最后部分还是堆栈已经被维护了?

5 个答案:

答案 0 :(得分:3)

经过一番搜索并看到生成了什么字节码后,我发现实际上看起来没有最终的块,也没有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块中更改了对象的内容,那么这些更改将会被反映出来在返回的值中。

答案 1 :(得分:2)

最后编译

try-finally语句的编译类似于try-catch的编译。在try语句之外传递控制之前,无论该传输是正常还是突然,因为抛出了异常,必须首先执行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语句,或者引发异常。

要了解更多有关javac如何解释finally块的信息。请参阅JLS - 3.13. Compiling finally

答案 2 :(得分:1)

当您输入返回值时,该方法已准备好返回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 打印出来。

答案 3 :(得分:1)

这是因为return语句的功能以及它与try语句的交互方式。

JLS的Section §14.17描述了return语句。

  

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

最后一句表示如果return语句的表达式正常评估,则return语句 abrutptly 完成。在您的示例中,由于try语句而导致return块突然终止,原因是return值为10 {i 已评估价值10)。

由于在您的示例中,return正在尝试使用finally块,因此JLS的Section §14.20.2告诉我们接下来会发生什么:

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

因此,由于返回语句被计算到值10,因为try块突然终止,并且因为finally块正常完成,所以该方法返回10.

答案 4 :(得分:0)

现在 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 的值将进入 i

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