奇怪的是最后的行为?

时间:2012-06-25 10:01:21

标签: java finally

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

7 个答案:

答案 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
}

影响返回值的其他方法是更改​​缓存对象的状态。例如,如果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);
}

答案 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语句阻止了要返回的对象而不是引用。您还可以尝试通过打印以下哈希码来验证这一点:

  • 从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);
            }
        }
    

答案 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 的值。