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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16030858/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 21:40:59  来源:igfitidea点击:

Why does changing the returned variable in a finally block not change the return value?

javatry-finally

提问by Devendra

I have a simple Java class as shown below:

我有一个简单的 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());
    }
}

And the output of this code is this:

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

Entry in finally Block
dev  

Why is snot overridden in the finallyblock, yet control printed output?

为什么s不在finally块中覆盖,但控制打印输出?

回答by Ted Hopp

The tryblock completes with the execution of the returnstatement and the value of sat the time the returnstatement executes is the value returned by the method. The fact that the finallyclause later changes the value of s(after the returnstatement completes) does not (at that point) change the return value.

try与所述的执行块完成return语句和的值s在时间return语句执行是由该方法返回的值。finally子句稍后更改s(在return语句完成后)的值这一事实(此时)不会更改返回值。

Note that the above deals with changes to the value of sitself in the finallyblock, not to the object that sreferences. If swas a reference to a mutable object (which Stringis not) and the contentsof the object were changed in the finallyblock, then those changes would be seen in the returned value.

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

The detailed rules for how all this operates can be found in Section 14.20.2 of the Java Language Specification. Note that execution of a returnstatement counts as an abrupt termination of the tryblock (the section starting "If execution of the try block completes abruptly for any other reason R...." applies). See Section 14.17 of the JLSfor why a returnstatement is an abrupt termination of a block.

所有这些操作的详细规则可以在 Java 语言规范的第 14.20.2 节中找到。请注意,return语句的执行算作try块的突然终止(以“如果 try 块的执行由于任何其他原因突然完成 R....”开头的部分适用)。请参阅JLS 的第 14.17 节了解为什么return语句是块的突然终止。

By way of further detail: if both the tryblock and the finallyblock of a try-finallystatement terminate abruptly because of returnstatements, then the following rules from §14.20.2 apply:

进一步详细说明:如果语句try块和语句finally块都try-finallyreturn语句而突然终止,则适用第 14.20.2 节中的以下规则:

If execution of the tryblock completes abruptly for any other reason R [besides throwing an exception], then the finallyblock is executed, and then there is a choice:

  • If the finallyblock completes normally, then the trystatement completes abruptly for reason R.
  • If the finallyblock completes abruptly for reason S, then the trystatement completes abruptly for reason S (and reason R is discarded).

如果try块的执行由于任何其他原因 R 突然完成 [除了抛出异常],然后finally块被执行,然后有一个选择:

  • 如果finally块正常完成,则try语句由于原因 R 突然完成。
  • 如果finally块因 S 原因突然完成,则try语句因 S 原因突然完成(并且 R 被丢弃)。

The result is that the returnstatement in the finallyblock determines the return value of the entire try-finallystatement, and the returned value from the tryblock is discarded. A similar thing occurs in a try-catch-finallystatement if the tryblock throws an exception, it is caught by a catchblock, and both the catchblock and the finallyblock have returnstatements.

结果是块中的return语句finally决定了整个try-finally语句的返回值,而try块中的返回值被丢弃。类似的事情发生在try-catch-finally语句中,如果try块抛出异常,被catch块捕获,并且catch块和finally块都有return语句。

回答by Tordek

Because the return value is put on the stack before the call to finally.

因为返回值是在调用 finally 之前放到栈上的。

回答by Mikhail

If we look inside bytecode, we'll notice that JDK has made a significant optimization, and foo()method looks like:

如果我们查看字节码内部,我们会注意到 JDK 进行了重大优化,而foo()方法如下所示:

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

And bytecode:

和字节码:

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 preserved "dev" string from being changed before returning. In fact here is no finally block at all.

java在返回之前保留了“dev”字符串不被更改。事实上,这里根本没有 finally 块。

回答by 0xCAFEBABE

There are 2 things noteworthy here:

这里有两点值得注意:

  • Strings are immutable. When you set s to "override variable s", you set s to refer to the inlined String, not altering the inherent char buffer of the s object to change to "override variable s".
  • You put a reference to the s on the stack to return to the calling code. Afterwards (when the finally block runs), altering the reference should not do anything for the return value already on the stack.
  • 字符串是不可变的。当您将 s 设置为“覆盖变量 s”时,您将 s 设置为引用内联字符串,而不是更改 s 对象的固有字符缓冲区以更改为“覆盖变量 s”。
  • 您将 s 的引用放在堆栈上以返回到调用代码。之后(当 finally 块运行时),改变引用不应该对堆栈中已经存在的返回值做任何事情。

回答by Frank

I change your code a bit to prove the point of Ted.

我稍微更改了您的代码以证明 Ted 的观点。

As you can see in the output sis indeed changed but after the return.

正如您在输出中s所看到的,确实发生了变化,但在返回之后。

public class Test {

public 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());
    System.out.println(obj.s);
}
}

Output:

输出:

Entry in finally Block 
dev 
override variable s

回答by Kiran Jujare

Technically speaking, the returnin the try block won't be ignored if a finallyblock is defined, only if that finally block also includes a return.

从技术上讲,return如果finally定义了一个块,则 try 块中的 不会被忽略,仅当该 finally 块还包含return.

It's a dubious design decision that was probably a mistake in retrospect (much like references being nullable/mutable by default, and, according to some, checked exceptions). In many ways this behaviour is exactly consistent with the colloquial understanding of what finallymeans - "no matter what happens beforehand in the tryblock, always run this code." Hence if you return true from a finallyblock, the overall effect must always to be to return s, no?

这是一个可疑的设计决定,回想起来可能是一个错误(很像默认情况下引用可以为空/可变,并且根据某些检查异常)。在许多方面,这种行为与对finally含义的通俗理解完全一致——“无论try块中事先发生了什么,始终运行此代码。” 因此,如果您从一个finally块返回 true ,则整体效果必须始终为 to return s,不是吗?

In general, this is seldom a good idiom, and you should use finallyblocks liberally for cleaning up/closing resources but rarely if ever return a value from them.

一般来说,这很少是一个好的习惯用法,您应该大量使用finally块来清理/关闭资源,但很少从它们返回值。

回答by Achintya Jha

Try this: If you want to print the override value of s.

试试这个:如果你想打印 s 的覆盖值。

finally {
    s = "override variable s";    
    System.out.println("Entry in finally Block");
    return s;
}