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};
它将s1
, s2
和s3
(它们是引用, 而不是对象)的值复制到数组中。 语句执行后,变量完全独立于数组。 更改任何s1
, s2
, s3
变量的值以引用不同的字符串根本不会影响数组的内容。
它与其他作业完全相同,例如
string x = "hello"; string y = x; x = null; // Doesn't affect y
和方法参数。
请注意,垃圾收集部分是一个红色鲱鱼 – 虽然显然理解这对于理解垃圾收集很重要,但事实并非如此。 重点是了解:
-
s1
,s2
和s3
以及数组的元素是引用 ,而不是对象。 - 创建数组时会复制这些值,因此之后更改变量不会产生任何影响
现在,再举一个例子,考虑一下:
StringBuilder sb = new StringBuilder("Hello"); StringBuilder[] array = { sb }; sb.append(" world"); System.out.println(array[0]);
这将打印“Hello world”,因为这里我们只有一个StringBuilder
对象 , sb
和array[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的一个建议。