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);
}
}
为什么从函数hii
返回后打印go()
,在finally块中值已更改为“hello”?
程序的输出是
finally value of q is hello
hii
答案 0 :(得分:27)
那是因为在更改finally块中q
的值之前,您返回了从q
评估的值。您返回了q
,评估了其值;然后你更改了q
块中的finally
,这不会影响加载的值;然后使用评估值完成返回。
不要写这样棘手的代码。如果它让那个写它的家伙感到困惑,想象一下它会导致下一个人的问题,几年后当你在其他地方的时候。
答案 1 :(得分:4)
return
返回值而不是引用。当return q;
在catch
中执行当前值q
时,将通过方法缓存引用作为其结果。因此,即使在finally
块中,您将使用新值重新分配q
,但它不会影响已由方法缓存的值。
如果您想要更新应该返回的值,则必须使用return
块中的其他finally
,例如
} finally {
q = "hello";
System.out.println("finally value of q is " + q);
return q;//here you set other value in return
}
影响返回值的其他方法是更改缓存对象的状态。例如,如果q
是List
,我们可以向其添加新元素(但请注意,更改状态与重新分配新实例不同,就像我们可以更改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);
}
答案 2 :(得分:2)
最后在返回之后但在方法实际返回给调用者之前执行。这类似于抛出。它发生在抛出之后和退出块之前。通过读取变量q,已在某些寄存器中设置返回值。如果q是可变的,你可以最终改变它,你会看到调用者的变化。为什么这样工作?首先,它可能是最不复杂的实现。二,它为您提供最大的灵活性。您可以使用显式返回覆盖finally中的返回值。默认情况下保留它可让您选择任一行为。
答案 3 :(得分:0)
[在EJP评论后编辑,我的第一个回复没有回答问题,也是错误的。]
现在我的答案应该是正确的解释,当try块和finally块正常完成时,返回q。并且返回值“hii”的原因在EJP答案中解释。我还在寻找JLS的解释。
查看JLS 14.20.2 Execution of try-catch-finally
首先执行try块,执行带有finally块的try语句。然后有一个选择:
如果try块的执行正常完成,则执行finally块,然后有一个选择:
如果finally块正常完成,则try语句正常完成 [...]
和JLS 14.17 The return Statement
带有Expression的return语句尝试将控制权转移给包含它的方法的调用者; Expression的值成为方法调用的值。更确切地说,执行这样的return语句首先评估Expression。如果表达式的评估由于某种原因突然完成,则返回语句因此而突然完成。如果表达式的评估正常完成,产生值V,则return语句突然完成,原因是返回值为V
和
前面的描述说“尝试转移控制”而不仅仅是“转移控制”,因为如果在try块包含return语句的方法或构造函数中有任何try语句(§14.20),那么这些语句的任何finally子句在将控制权转移给方法或构造函数的调用者之前,try语句将按顺序执行,最内层到最外层。突然完成finally子句可能会破坏由return语句启动的控制权转移。
答案 4 :(得分:0)
尝试使用StringBuffer而不是String,您将看到更改....似乎return语句阻止了要返回的对象而不是引用。您还可以尝试通过打印以下哈希码来验证这一点:
从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);
}
}
答案 5 :(得分:0)
什么是最终阻止?
-By definition from Java“finally块总是在try块退出时执行。这确保即使发生意外异常也会执行finally块。”
因此,只要它存在于try块并打印到System.out.print(a)行,它就会打印出“q的最终值为hello”。并打印方法go()返回的值。
如果你有像netbeans或eclipse这样的调试器,可以通过保持断点和唤醒代码来分析它。
答案 6 :(得分:-1)
好吧,我发现如下,
返回实际返回一个值,并在执行转到最后之前将其复制到String a=obj.go();
。
让我们通过以下实验来验证它。
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对象,而不是创建新的StringBuffer对象。所以存储在变量 a 中的值也会受到操纵,这可以在print语句中看到。
3.在第三种情况下,在执行转到finally之前,int的值被复制到变量 a 中。因此 a 得到的值为1.然后最后我们改变了 q 的值,这无论如何都不会改变 a 的值。