为什么更改finally块中的返回变量不会更改返回值?

问题

我有一个简单的Java类,如下所示:

public class Test {

    private String s;

    public String foo() {
        try {
            s = "dev";
            return s;
        } 
        finally {
            s = "override variable s";
            System.out.println("Entry in finally Block");  
        }
    }

    public static void main(String[] xyz) {
        Test obj = new Test();
        System.out.println(obj.foo());
    }
}

这段代码的输出是这样的:

Entry in finally Block
dev

为什么s不会在56284616块中被覆盖,而是控制打印输出?


#1 热门回答(163 赞)

try块在执行return语句时完成,并且在return语句执行时的值s是该方法返回的值。 finallyclause稍后更改值434849356(在return语句完成之后)的事实不会(在此时)更改返回值。

请注意,以上内容处理的是对finally块中的值500032437自身的更改,而不是对s引用的对象的更改。 Ifs是对可变对象的引用(其中String不是)并且在finally块中更改了对象的内容,那么将在返回的值中看到这些更改。

所有这些操作的详细规则可以在Section 14.20.2 of the Java Language Specification找到。请注意,areturn声明的执行计为try块的突然终止(开始的部分"如果执行块因任何其他原因而突然完成R ...."适用)。请参阅435577609,了解为什么areturn语句是块的突然终止。

进一步详细说明:如果由于return语句而突然终止a62929604语句的try块和finally块,则适用§14.20.2的以下规则:

如果try块的执行由于任何其他原因而突然完成R [除了抛出异常],则执行finally块,然后有一个选择:如果finally块正常完成,则try语句突然完成R.如果finally块因为原因S而突然完成,则try语句突然完成,原因是S(并且原因R被丢弃)。

结果是finallyblock中的return语句确定整个try-finally语句的返回值,并且丢弃tryblock的返回值。如果try块抛出异常,它会被acatch块捕获,并且catchblock和finallyblock都有return语句,则类似的事情发生在try-catch-finally语句中。


#2 热门回答(62 赞)

因为返回值在调用finally之前放在堆栈上。


#3 热门回答(33 赞)

如果我们查看字节码内部,我们会注意到JDK已经进行了重大优化,并且41222523 foo()**方法看起来像:

String tmp = null;
try {
    s = "dev"
    tmp = s;
    s = "override variable s";
    return tmp;
} catch (RuntimeException e){
    s = "override variable s";
    throw e;
}

和字节码:

0:  ldc #7;         //loading String "dev"
2:  putstatic   #8; //storing it to a static variable
5:  getstatic   #8; //loading "dev" from a static variable
8:  astore_0        //storing "dev" to a temp variable
9:  ldc #9;         //loading String "override variable s"
11: putstatic   #8; //setting a static variable
14: aload_0         //loading a temp avariable
15: areturn         //returning it
16: astore_1
17: ldc #9;         //loading String "override variable s"
19: putstatic   #8; //setting a static variable
22: aload_1
23: athrow

java保留"dev"字符串在返回之前被更改。事实上,这里根本没有最终的阻止。