finally块中将对象引用设置为null

12
public void testFinally(){
System.out.println(setOne().toString());

}

protected StringBuilder setOne(){
StringBuilder builder=new StringBuilder();
try{
builder.append("Cool");
return builder.append("Return");
}finally{
builder=null; /* ;) */
}
}

为什么输出结果是"CoolReturn"而不是null?

谨致问候,
Mahendra Athneria

3个回答

14
表达式在返回语句中被求值,并且这是将要返回的值。finally块在返回语句的表达式求值部分之后执行。
当然,finally块可以修改返回值所引用的对象的内容-例如:
finally {
  builder.append(" I get the last laugh!");
}

如果这样做,控制台输出将是"CoolReturn I get the last laugh!" - 但它不能改变实际返回的值。


2
@Darin:因为 builder 是一个引用。将引用设置为 null 将取消该引用,但它所引用的对象仍然存在,直到被垃圾回收。另一方面,append 修改了对象(或其属性之一)。 - Powerlord
3
@Darin: append方法返回调用它的变量的链接。因此,return builder.append("some value"); 实际上将 "some value" 添加到构建器中并返回构建器作为对象的链接,在finally块中更改。当您编写builder = null时,您只是更改了builder指向的位置,但没有更改旧链接下的值。 - Maxym
@Maxym - 正如你所说,通过编写builder=null,我们只是改变了builder的指向。我同意。但是作为回报,我们正在返回引用,现在它指向null。因此,它应该使用新引用指向的值。你明白我的意思吗? - Mahendra Athneria
@Mahendra 当程序执行 "return ..." 时,它会将指向堆栈中对象的链接写入,并且您无法通过更改构建器引用来更改它,因为该引用已经被评估并推送到堆栈中。也许这种方式更好地解释了这个问题。 - Maxym
@Maxym - 你的意思是在return语句中使用了包含值“CoolReturn”的builder旧引用,而在finally块中我们修改了builder引用,这对于return来说是未知的。因此它返回旧值。但是如果我在finally块中添加一些内容,例如"builder.append("some new value")",那么它会将其附加到旧引用值中,即"CoolReturn some new value"。并且它将返回"CoolReturn some new value"。非常好的解释@Maxym。我的疑惑已经解决。谢谢 :-) - Mahendra Athneria
显示剩余3条评论

2

显然,看起来应该是null,但由于Java中的按引用传递的概念,它是这样的:

1> 执行builder.append("Return")...行并返回builder引用的副本,通过按引用传递将其返回到testFinally()方法

2> 在finally块中执行builder=null时,builder引用被取消引用,但实际上在堆中引用builder之前引用的对象仍然存在于堆中,而返回的builder引用的副本(也是指向同一对象的引用)仍然存在,并且保留了“CoolReturn”值,这就是为什么它打印返回的值的原因。


0

finally 块用于在 try 块执行后进行 "清理"。由于您已经返回了引用,因此无法以这种方式更改它。


我认为那有点误导性——例如,如果finally块抛出异常,那么它仍然会被抛出。我认为更清晰的做法是将返回值的评估(发生在finally块之前)和控制流传递回调用方方法的操作(发生在finally块之后)分开。 - Jon Skeet
你说得对,我会改掉我的措辞。但是无论如何,你的回答都很好;-) - morja

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接