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
Why does changing the returned variable in a finally block not change the return value?
提问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 s
not overridden in the finally
block, yet control printed output?
为什么s
不在finally
块中覆盖,但控制打印输出?
回答by Ted Hopp
The try
block completes with the execution of the return
statement and the value of s
at the time the return
statement executes is the value returned by the method. The fact that the finally
clause later changes the value of s
(after the return
statement 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 s
itself in the finally
block, not to the object that s
references. If s
was a reference to a mutable object (which String
is not) and the contentsof the object were changed in the finally
block, then those changes would be seen in the returned value.
请注意,以上处理的s
是finally
块中自身值的更改,而不是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 return
statement counts as an abrupt termination of the try
block (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 return
statement 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 try
block and the finally
block
of a try-finally
statement terminate abruptly because of return
statements, then the following rules from §14.20.2 apply:
进一步详细说明:如果语句try
块和语句finally
块都try-finally
因return
语句而突然终止,则适用第 14.20.2 节中的以下规则:
If execution of the
try
block completes abruptly for any other reason R [besides throwing an exception], then thefinally
block is executed, and then there is a choice:
- If the
finally
block completes normally, then thetry
statement completes abruptly for reason R.- If the
finally
block completes abruptly for reason S, then thetry
statement 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 return
statement in the finally
block determines the return value of the entire try-finally
statement, and the returned value from the try
block is discarded. A similar thing occurs in a try-catch-finally
statement if the try
block throws an exception, it is caught by a catch
block, and both the catch
block and the finally
block have return
statements.
结果是块中的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 s
is 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 return
in the try block won't be ignored if a finally
block 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 finally
means - "no matter what happens beforehand in the try
block, always run this code." Hence if you return true from a finally
block, 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 finally
blocks 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;
}