我尝试了以下代码,其中有一个名为data
的final
实例变量。这是使用 int[]
参数在构造函数中实例化的。如果 int[] 数组的元素发生更改,则更改将反映在实例变量中,并显示在show()'s
输出中。但是,如果我将外部数组设置为 null 或新数组,则更改不会反映在 show(( 输出中。
为什么会这样? 如果外部数组由 ext[0]=x 更改,则更改显示在 如果 ext 引用设置为新对象,则不会发生 inst.variable.It。
public class MutabilityTest {
public static void main(String[] args) {
int[] ext = new int[] {1,2,3,4,5};
FMutable xmut = new FMutable(ext);
mut.show(); //shows [1,2,3,4,5]
System.out.println("changed ext array");
ext[0] = 99;
System.out.println("ext:"+Arrays.toString(ext)); //[99,2,3,4,5]
mut.show(); //shows [99,2,3,4,5]
System.out.println("set ext array to new");
ext = new int[]{8,8,8,8}
System.out.println("ext:"+Arrays.toString(ext)); //[8,8,8,8]
mut.show();//expected [8,8,8,8] but got [99,2,3,4,5]
ext = null;
System.out.println("ext:"+Arrays.toString(ext)); //null
mut.show(); //shows same [99,2,3,4,5]
}
}
class FMutable{
private final int[] data;
public FMutable(int[] indata){
this.data = indata;
}
public void show(){
System.out.println("XMutable:"+Arrays.toString(this.data));
}
}
最终data
或其他地方的字段无关。您可以更简单地看到完全相同的效果:
int[] x = { 1, 2, 3, 4, 5 };
int[] y = x;
y[0] = 10;
System.out.println(Arrays.toString(x)); // 10, 2, 3, 4, 5
y = new int[] { 3, 3, 3, };
System.out.println(Arrays.toString(x)); // 10, 2, 3, 4, 5
区分变量、变量值和对象非常重要。将变量想象成一张纸。它可以写一个基元值,或者一个房子地址。
该行:
int[] y = x;
或在示例代码中:
this.data = indata;
只需将一张纸上写的内容复制到一张新纸上即可。这只是"房子"(在本例中为数组对象(的地址。
现在这两张纸本身是独立的——改变其中一张纸上写的内容不会改变另一张纸上写的内容。但是,如果您按照一张纸上的地址去房子,进行更改(例如,粉刷前门(,然后按照另一张纸上的地址去房子(当它们仍然相同时(,那么是的,你会看到新粉刷的门。这就是您对数组本身进行更改时所看到的。
ext
只是另一个引用,指向与实例数组相同的内存位置data
。如果将ext
设置为 null,则只会将ext
引用设置为 null
,而不会设置data
数组。
final int[] data
这意味着您的数组 obejct 是最终的(您可以初始化一次,之后不能更改(,但index's
不是。
您已通过构造函数初始化。
public FMutable(int[] indata){
this.data = indata;
}
ext
是指向同一对象的另一个参考。对同一对象的任何更改也会对final data
字段产生影响,因为两者都引用相同的Object
。
但是,由于参考数据是最终的,因此您无法重新初始化。ext = new int[]{8,8,8,8}
将创建一个新数组,ext 将指向该数组。
ext = new int[]{8,8,8,8}
为变量ext
创建一个新数组,但不会影响对象中的字段data
。请注意,如果data
不是最终的,也会发生相同的效果!
final
仅表示您可以设置一次引用,但仍然可以操作对象。