因此,我是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分配指向该对象空间的指针。 (这样对吗?)

随后,在循环的每个连续迭代中,乘法方法为新对象实例分配额外的堆,以包含所得的不可变值(例如,分配第4次迭代结果的堆,并为新对象分配值256)和biValue被分配了一个引用新对象的指针。 (这也正确吗?)

(顺便说一句,根据我的收集,以前的对象的堆空间只是孤立的?还是立即执行垃圾回收?否则,似乎在某些情况下可能很快用完了堆。)

所以我能正确看到这一点,还是我错过了重要的细节,或者...?

谢谢!

最佳答案

您对每次调用multiply如何创建一个新的BigInteger对象是正确的,并且biValue的值在每次迭代中都会改变。

当我们说不变性时,我们的意思是对象是不能变异的,而不是变量。可以使用final修饰符声明一个不能突变的变量。

是的,许多不可变的类可能“似乎”是可变的。在这种情况下,看似变异的方法将创建一个新对象。

关于循环的先前迭代中BigInteger对象的命运,是的,它们将被垃圾回收。但是,这种情况发生的时间是不确定的。请注意,它可能会填满堆,这不是因为GC没有收集未使用的对象,而是因为数量太大。

09-28 04:50