Java:将引用设置为null不会影响对象

5
我有一个简单的问题。在下面的代码中,为什么即使我在之前将s3设为null,它的值仍然被打印出来呢?似乎垃圾回收器没有被调用。
public class Test {
    public static void main(String[] args) {
         String s1 = "abc", s2 = "def", s3 = "ghj";

         String sarr[] = {s1, s2, s3};
         s3 = null;

         System.gc(); 

         for(int i = 0; i < sarr.length; i++) {
             System.out.print(sarr[i] + " "); //prints abc def ghj
         }
    }
 }

任何想法都将不胜感激。

2
s3s的值没有被打印出来。"ghj"的值被打印出来了。 - Ingo
1
System.gc()会要求JVM安排垃圾回收,但并不意味着实际的垃圾回收会在主线程执行“for”循环之前发生。然而,我认为这不是你问题的原因。 - DaveH
6个回答

11

当你写下以下代码:

// Moved [] to make it more idiomatic
String[] sarr = {s1, s2, s3};

这段话涉及到IT技术相关内容。它将s1s2s3的值(它们是引用,而不是对象)复制到数组中。执行完该语句后,变量与数组完全独立。更改任何一个s1s2s3变量的值以引用不同的字符串不会影响数组的内容。这与其他赋值操作完全相同,例如:
string x = "hello";
string y = x;
x = null; // Doesn't affect y

以及方法参数。

注意,垃圾回收部分在这里是一个红色的诱饵 - 虽然显然理解这一点对于理解垃圾回收很重要,但反之则不成立。重要的是要理解:

  • s1s2s3 的值以及数组的元素是引用,而不是对象。
  • 在创建数组时,这些值被复制,因此之后改变变量不会有任何影响。

现在,为了进一步说明这个例子,考虑:

StringBuilder sb = new StringBuilder("Hello");
StringBuilder[] array = { sb };

sb.append(" world");
System.out.println(array[0]);

这将打印出“Hello world”,因为我们只有一个StringBuilder对象sbarray [0]都指向它。改变该对象内部的数据(与将sb更改为引用不同的对象)使得无论如何访问该对象,都会看到更改。这就像我把我的房子地址给两个人一样 - 如果其中一个人把我的房子涂成红色,另一个人也会看到。

9
你已经重新分配了s3指向其他内容(null),但数组内的引用仍然指向原始值(“ghj”)。 s3不是指向数组位置的常量指针。 s3的值(字符串值“ghj”的引用)被复制到数组中。变量本身并没有连接,并且可以自由更改以独立于你对数组内容所做的任何操作参考另一个String对象。

4

请理解垃圾回收机制无法强制执行,你只能建议其运行。

为了回答你的问题,你需要记住引用和值之间的区别。当你实例化s3时,你将它指向“ghj”。当你实例化sarr[2]时,你也将它指向“ghj”。然后你将s3引用置为空,但是sarr[2]仍然指向堆上的“ghj”字符串。


垃圾收集仅在没有任何指向它们的引用的对象上发生。 - Brandon Buck

1
我猜测字符串“ghj”被添加到了字符串池中。当你将s3 = null时,s3变量不再引用字符串池中的ghj,但是你的数组元素sarr [2]仍然引用它,因此它仍然可以正确打印。

1
Java中的String是不可变的。一旦你创建了它,它就不会改变。你所做的是创建一个字符串(通过代码中的"ghj"字面量),并将该字符串的引用赋给一个变量s3
之后,您将三个变量放入一个数组中。基本上发生的事情是三个引用在数组中占据了一个位置。
然后,您将变量s3设置为指向null。存储在s3中的引用被从其中移除,并被一个空指针替换。但是,该引用仍存在于数组中。对字符串的引用被复制到数组中,而不是实际的字符串或变量本身。
因此,当您打印数组内容时,对"ghj"字符串的引用仍然存在,并将被使用。由于该引用仍然处于活动状态,它不会被垃圾回收。

还要注意,调用System.gc()表示您希望垃圾回收器运行。但是不能保证在方法返回或任何时间限制内都会发生这种情况。


0

由于数组仍然引用了s3的值。

值得注意的是,并不能保证System.gc()会实际调用垃圾回收器。它只是对JVM的建议。


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