16
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
4

7 回答 7

27

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

不要写这样棘手的代码。如果它让编写它的人感到困惑,想象一下它将给下一个人带来的问题,几年后你在其他地方。

于 2012-06-25T10:07:11.890 回答
4

return返回不可变。当在变量的部分中return q;执行时,变量的值被缓存为方法结果。就像方法记住的当前值一样,但在它返回之前,它还让我们完成了一些事情。catchqq

因此,即使在finally块中,您将为其分配新值q也不会更改已通过方法缓存的值。

如果要更新返回值,则必须使用另一个returninfinally块,例如

} finally {
    q = "hello";
    System.out.println("finally value of q is " + q);

    return q; // <--- this will set new value which should be returned
}

影响返回“值”的其他方式是改变它的状态

例如,如果q是 a List,您可以在 finally 块中添加新元素

} 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);
}
于 2012-06-25T10:21:31.647 回答
2

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

于 2012-06-25T10:22:58.603 回答
0

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

看看JLS 14.20.2 执行 try-catch-finally

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

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

JLS 14.17 返回声明

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

和:

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

于 2012-06-25T10:18:26.213 回答
0

尝试使用 StringBuffer 而不是 String ,您会看到变化......似乎 return 语句阻塞了要返回的对象而不是引用。您还可以尝试通过打印以下的哈希码来验证这一点:

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

    公共静态无效主要(字符串[]参数){

        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);
            }
        }
    
于 2012-06-25T11:15:28.907 回答
0

什么是最终块?

-根据 Java 的定义, “finally 块总是在 try 块退出时执行。这确保了 finally 块即使发生意外异常也会被执行。”

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

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

于 2013-01-12T19:51:33.193 回答
-1

嗯,我发现如下,

String a=obj.go();Return 实际上返回一个值,并且在执行到 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);
     }
}

程序的输出是

最后 q 的值是 hello

你好

如果我们采用 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 的值是 hello

你好

最后,如果我们采用 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 的复制地址,然后执行到最后更改 String 的地方。但是由于在字符串的情况下,我们无法操作任何字符串,因此会构造一个新的字符串。因此,在变量中保存了原始字符串地址,并打印出来。

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

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

于 2012-06-26T06:42:04.423 回答