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

我有一个简单的问题。 在下面的代码中,为什么s3的值仍然打印,尽管之前我将它设置为null。 似乎gargbage收集器不会被调用。

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

任何想法将不胜感激。

当你写:

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

它将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以引用不同的对象不同)使得更改可见,但是您可以使用它。 这就像我把我家的地址给两个人 – 如果其中一个人把我的房子涂成红色,另一个人也会看到它。

你已经重新分配了s3来指向别的东西(null),但是数组中的引用仍然指向原始值(“ghj”)。

s3不是指向数组位置的常量指针。 s3的值(对字符串值“ghj”的引用 )被复制到数组中。 变量本身没有连接,可以自由更改以引用另一个String对象,而与您对数组内容的操作无关。

明白你不能强制垃圾收集器运行; 你只能建议它运行。

要回答您的问题,您需要记住参考和价值之间的差异。 实例化s3时,将其指向“ghj”。 当你实例化sarr [2]时,你也将它指向“ghj”。 然后你将s3引用清空,但sarr [2]仍然指向堆中的“ghj”字符串。

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

Java String是不可变的。 一旦你做到了,它就不会改变。 你所做的是创建一个String(隐式地,通过代码中的"ghj"文字)并将该字符串的引用分配给变量s3

之后,您将三个变量放入一个数组中。 基本上发生的是三个引用在数组中给出了一个位置。

然后,将变量s3设置为null。 存储在s3引用将从中删除,替换为空指针。 但是,该引用仍然在数组中。 对String的引用被复制到数组中,而不是实际的String或变量本身。

因此,当您打印出数组内容时,对"ghj"字符串的引用仍然存在并将被使用。 由于此引用仍然可以主动访问,因此不会进行垃圾回收。

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

因为s3值仍然是从数组中引用的。

还值得注意的是,从来没有保证System.gc()实际上会调用垃圾收集器。 这只是对JVM的一个建议。