我有一个简单的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
块中没有覆盖finally
,而是控制打印输出?
答案 0 :(得分:164)
try
块完成return
语句的执行,s
语句执行时return
的值是方法返回的值。 finally
子句稍后更改s
的值(在return
语句完成之后)不会(在此时)更改返回值。
请注意,上述内容涉及s
块中finally
本身值的更改,而不是s
引用的对象的更改。如果s
是对可变对象的引用(String
不是)并且finally
块中的对象的内容已更改,那么这些更改将在返回的值中看到。
有关如何运作的详细规则可在Section 14.20.2 of the Java Language Specification中找到。请注意,return
语句的执行计为try
块的突然终止(开始的部分“如果由于任何其他原因R ....执行try块突然完成/ em>“适用”。有关return
语句突然终止块的原因,请参阅Section 14.17 of the JLS。
进一步详细说明:如果try
块和finally
阻止
由于try-finally
语句,return
语句突然终止,因此§14.20.2中的以下规则适用:
如果
try
块的执行由于任何其他原因而突然完成R [除了抛出异常],则执行finally
块,然后有一个选择:
- 如果
finally
块正常完成,则try
语句突然完成,原因为R。- 如果
finally
块因原因S突然完成,则try
语句突然完成,原因为S(原因R被丢弃)。
结果是return
块中的finally
语句确定了整个try-finally
语句的返回值,并且丢弃了try
块中返回的值。如果try-catch-finally
块抛出异常,它被try
块捕获,catch
块和{{1},则会在catch
语句中发生类似的情况阻止finally
个语句。
答案 1 :(得分:63)
因为在调用finally之前将返回值放在堆栈上。
答案 2 :(得分:33)
如果我们查看字节码内部,我们会注意到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;
}
字节码:
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”字符串在返回之前被更改。事实上,这根本没有最终阻止。
答案 3 :(得分:22)
这里有两件值得注意的事情:
答案 4 :(得分:13)
我改变你的代码以证明特德的观点。
正如您在输出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);
}
}
输出:
Entry in finally Block
dev
override variable s
答案 5 :(得分:6)
从技术上讲,如果定义了return
块,则不会忽略try块中的finally
,只有当finally块还包含return
时才会被忽略。
这是一个可疑的设计决定,回想起来可能是一个错误(很像默认情况下引用是可空的/可变的,并且根据一些情况,检查异常)。在许多方面,这种行为与对finally
意味着什么的口语理解完全一致 - “无论事先在try
块中发生什么,总是运行此代码。”因此,如果您从finally
块返回true,则整体效果必须始终为return s
,不是吗?
一般来说,这很少是一个很好的习惯用法,你应该大量使用finally
块来清理/关闭资源,但很少从它们返回一个值。
答案 6 :(得分:0)
试试这个:如果你想打印s的覆盖值。
finally {
s = "override variable s";
System.out.println("Entry in finally Block");
return s;
}