3

根据 Java 语言规范,第 14.20.2 节

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

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

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

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 块

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

4

5 回答 5

3

经过一番搜索并查看生成了哪些字节码后,我发现实际上并没有看起来的 finally 块,也没有 JVM 生成的 jump 或 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 块中发生了更改,那么这些更改也会反映在返回值中。

于 2013-10-23T12:10:35.557 回答
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。最后编译

于 2013-10-23T10:28:18.503 回答
1

当你输入return时,该方法已经准备好返回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 个。

于 2013-10-23T10:23:55.493 回答
1

这是因为 return 语句函数的方式以及它与 try 与 finally 语句交互的方式。

JLS的第 14.17 节描述了 return 语句。

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

最后一句表示如果 return 语句的表达式被正常计算,那么 return 语句会突然完成。在您的示例中,由于语句和原因是值 10(被评估为值 10) ,try块突然终止。returnreturni

由于在您的示例中return是 try with finally 块,因此 JLS 的第 14.20.2 节告诉我们接下来会发生什么:

  • 如果 try 块的执行由于任何其他原因 R 突然完成,则执行 finally 块,然后有一个选择:
    • 如果 finally 块正常完成,则 try 语句由于原因 R 突然完成。
    • 如果 finally 块由于原因 S 突然完成,则 try 语句由于原因 S 突然完成(并且原因 R 被丢弃)。

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

于 2013-10-23T10:53:29.940 回答
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

*附带说明: * 永远不要编写除资源清理之外的任何业务逻辑。这会破坏可读性并导致错误。

于 2013-10-23T10:23:13.140 回答