奇怪的是最后的行为?

public class Test2 { public static void main(String[] args) { Test2 obj=new Test2(); String a=obj.go(); System.out.print(a); } public String go() { String q="hii"; try { return q; } finally { q="hello"; System.out.println("finally value of q is "+q); } } 

为什么从函数go()返回后打印hii ,在finally块中值已更改为“hello”?

该计划的输出是

 finally value of q is hello hii 

那是因为在更改finally块中的q值之前,您返回了一个从q求值的值。 你返回了q ,评估了它的价值; 然后你在finally块中更改了q ,这不会影响加载的值; 然后使用评估值完成返回。

不要写这样棘手的代码。 如果它让写作它的人感到困惑,想象一下它会导致下一个人的问题,几年后当你在其他地方的时候。

return返回不是引用。 当return q;catch执行当前值的q引用由方法缓存为结果。 因此,即使在finally块中,您将使用新值重新分配q ,但它不会影响已由方法缓存的值。

如果你想更新应该返回的值,你将不得不在你的finally块中使用另一个return

 } finally { q = "hello"; System.out.println("finally value of q is " + q); return q;//here you set other value in return } 

影响返回值的其他方法是更改​​缓存对象的状态 。 例如,如果qList我们可以向它添加新元素(但请注意,更改状态与重新分配新实例不同,就像我们可以更改final变量的状态一样,但我们无法重新分配它)。

 } finally { q.add(new Element); //this will place new element (update) in List //object stored by return because it is same object from q reference System.out.println("finally value of q is " + q); } 

最后在返回之后但在方法实际返回调用者之前执行。 这类似于抛出。 它发生在抛出之后和退出块之前。 通过读取变量q,已经在某个寄存器中设置了返回值。 如果q是可变的,你可以最终改变它,你会看到调用者的变化。 为什么这样工作? 首先,它可能是最不复杂的实现。 二,它给你最大的灵活性。 您可以使用显式返回覆盖finally中的返回值。 默认情况下保留它可让您选择任一行为。

[ 在EJP的评论后编辑,我的第一个回复没有回答问题,也是错误的。 ]
现在我的回答应该是正确的解释,当try块和finally块正常完成时,返回q。 并且返回值“hii”的原因在EJP答案中解释。 我还在寻找JLS的解释。

看看JLS 14.20.2执行try-catch-finally

通过首先执行try块来执行具有finally块的try语句。 然后有一个选择:

如果try块的执行正常完成,则执行finally块,然后有一个选择:
如果finally块正常完成,则try语句正常完成。
[…]

和JLS 14.17返回声明

带有Expression的return语句尝试将控制权转移给包含它的方法的调用者; Expression的值成为方法调用的值。 更准确地说,执行这样的return语句首先评估Expression。 如果表达式的评估由于某种原因突然完成,则返回语句因此而突然完成。 如果表达式的评估正常完成,产生值V,则return语句突然完成,原因是返回值为V

和:

前面的描述说“尝试转移控制”而不仅仅是“转移控制”,因为如果在try块包含return语句的方法或构造函数中有任何try语句(§14.20),那些try语句的finally子句将会在控制转移到方法或构造函数的调用者之前,按顺序执行,从最里面到最外面。 突然完成finally子句可以破坏由return语句启动的控制转移。

尝试使用StringBuffer而不是String,您将看到更改….似乎return语句阻止了要返回的对象而不是引用。 您还可以尝试通过打印以下哈希码来validation这一点:

  • 从go()返回的对象
  • 最后的对象
  • 从main()打印的对象

    public static void main(String [] args){

      Test obj=new Test(); StringBuffer a=obj.go(); System.out.print(a); } public StringBuffer go() { StringBuffer q=new StringBuffer("hii"); try { return q; } finally { q=q.append("hello"); System.out.println("finally value of q is "+q); } } 

什么是最终阻止?

– 从Java定义 “finally块总是在try块退出时执行。这确保即使发生意外exception也会执行finally块。”

因此,只要它存在于try块并打印到System.out.print(a)行,就会打印出“q的最终值是hello”。 并打印方法go()返回的值。

如果你有像netbeans或eclipse这样的调试器,可以通过保持断点和唤醒代码来分析它。

那么,我发现如下,

返回实际返回一个值,并将其复制到String a=obj.go(); ,在执行之前转到最后

让我们通过以下实验来validation它。

 public class Test2 { public static void main(String[] args) { Test2 obj=new Test2(); String a=obj.go(); System.out.print(a); } public String go() { String q="hii"; try { return q; } finally { q="hello"; System.out.println("finally value of q is "+q); } } 

该计划的输出是

最后q的值是你好

HII

如果我们采用StringBuffer而不是String,如下所示,

 public class Test2 { public static void main(String[] args) { // TODO Auto-generated method stub Test2 obj=new Test2(); StringBuffer a=obj.go(); System.out.print(a); } public StringBuffer go(){ StringBuffer q=new StringBuffer("hii"); try{ return q; } finally{ q.replace(0, q.length(), "hello"); System.out.println("finally value of q is "+q); /*return q1;*/ } } } 

输出结果是,

最后q的值是你好

你好

最后如果我们采用int而不是String,如下所示,

 public class Test2 { public static void main(String[] args) { // TODO Auto-generated method stub Test2 obj=new Test2(); int a=obj.go(); System.out.print(a); } public int go(){ int q=1; try{ return q; } finally{ q=2; System.out.println("finally value of q is "+q); /*return q1;*/ } } } 

输出是

最后q的值是2

1

  **Ananlysis** 

1.在第一种情况下,返回变量a中的String的复制地址,然后执行转到最后更改字符串的地方。 但是因为在Strings的情况下,我们不能操纵任何String构造一个新的String。 因此,在变量中保存原始字符串地址,然后打印。

2.在第二种情况下,在变量a中返回StringBuffer的复制地址,最后操作此StringBuffer对象,而不是创建新对象。 所以存储在变量a中的值也会被操纵,这在print语句中可以看到。

3.在第三种情况下,int的值被复制到变量a中 ,然后执行到最后。 因此得到的值为1.然后最后我们改变了q的值,它无论如何都没有改变a的值。