所以,我是Java的新手,正在努力理解不可变对象是如何实现的,以及为什么它们看起来仍然是可变的。我查看了很多资料,似乎都在“暗示”发生了什么,但我只想澄清一下是否我走在了正确的道路上。
使用以下简单的示例:
import java.math.*;
class BIMutability {
public static void main(String args[]) {
BigInteger biValue = new BigInteger("2");
for (int i=1; i<10; i++) {
System.out.println(i + ". biValue = " + biValue);
biValue = biValue.multiply(biValue);
}
}
}
当运行时,会产生以下结果:
1. biValue = 2
2. biValue = 4
3. biValue = 16
4. biValue = 256
5. biValue = 65536
6. biValue = 4294967296
7. biValue = 18446744073709551616
8. biValue = 340282366920938463463374607431768211456
9. biValue = 115792089237316195423570985008687907853269984665640564039457584007913129639936
表面上看,biValue似乎是可变的。但实际上并非如此,我的理解是:biValue本质上是一个指针变量。在运行时进行实例化时,为BigInteger类的对象分配了堆空间,调用了它的构造函数(其中包括根据字面值“2”初始化对象的值),最后将一个指向对象空间的指针赋给了biValue。(这样说对吗?)
随后,在循环的每次迭代中,multiply方法会为新对象实例分配额外的堆空间以包含结果不可变的值(例如,为第四次迭代的结果分配堆空间,并将新对象赋值为256),然后将一个指向新对象的指针赋给biValue。(这也正确吗?)
(顺便说一下,据我所知,先前的对象的堆空间只是被遗弃了吗?还是立即执行了垃圾收集?如果没有,那么在某些情况下,您可能很快就会耗尽堆空间。)
所以,我是否正确地理解了这一点,或者我漏掉了重要细节,或者……?
谢谢!
biValue
被设置为其他值时,之前的BigInteger
对象将不再有指向它的引用,因此它可以被垃圾回收。它不会立即被垃圾回收,但 JVM 会为你处理这个问题。 - Jacob G.biValue
是一个变量。在 Java 程序中,每个变量都保存着一个原始值(例如int
或double
),或者它保存着一个对象引用。在 Java 中(不像 C++),对象从来不会存储在变量中。它们只能在堆上找到。在你的程序中,biValue
是一个可变的变量,它保存着对一个不可变对象的引用。如果你想要一个不可变的变量,你可以将其声明为final
,或者你可以简单地不编写任何分配它的代码。 - Solomon Slow