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

我有一个简单的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 

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

try块完成后执行return语句, return语句执行时的s值是方法返回的值。 finally子句后来更改s的值(在return语句完成之后)不会(在这一点上)更改返回值。

请注意,上述处理在finally块中s本身的值的更改,而不是引用的对象。 如果s是一个可变对象的引用(该String不是),并且对象的内容finally块中被改变,那么这些改变将在返回的值中看到。

所有这些操作的详细规则可以在Java语言规范的第14.20.2节中find。 请注意,执行return语句将被视为try块的突然终止(“ 如果尝试块的执行由于任何其他原因而突然完成,则开始”一节)。 请参阅JLS的14.17节,了解为什么return语句是块的突然终止。

通过进一步的细节:如果try-finally语句的try块和finally块由于return语句而突然终止,则来自§14.20.2的以下规则适用:

如果try块的执行由于任何其他原因而突然完成[除了抛出exception],那么finally块将被执行,然后有一个select:

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

结果是finally语句块中的return语句决定了整个try-finally语句的返回值,而try语句块返回的值被丢弃。 try-catch-finally语句中会出现类似的情况,如果try块引发exception,被catchcatch ,并且catch块和finally块都有return语句。

因为在最后调用之前将返回值放在堆栈上。

如果我们看一下字节码,我们会注意到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”string。 其实这里根本就不是最后一块。

这里有两件值得注意的事情:

  • string是不可改变的。 当你设置s为“覆盖variabless”时,你将s设置为引用内联string,而不是改变s对象的固有char缓冲区以改变为“覆盖variabless”。
  • 你把一个对栈的引用返回到调用代码。 之后(当finally块运行时),改变引用不应该为已经在堆栈上的返回值做任何事情。

我改了一下你的代码来certificate特德的观点。

正如你在输出中看到的那样,确实已经改变了,但是在返回之后。

 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 

从技术上讲,如果定义了finally块,那么try块中的return不会被忽略,只有在finally块中还包含return

这是一个可疑的devise决策,可能是一个回顾过程中的错误(很像引用是默认可空/可变的,根据一些,检查exception)。 在许多方面,这种行为与对finally意义的口语理解是完全一致的 – “不pipe事先在try块中发生了什么,总是运行这个代码”。 因此,如果你从finally块返回true,总体效果必须总是return s ,否?

一般来说,这是一个很好的习惯用法,你应该使用finally块来清理/closures资源,但是很less从它们那里返回一个值。

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

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