Java 为什么这个类被认为是可变的?

Java 为什么这个类被认为是可变的?,java,arrays,immutability,mutability,Java,Arrays,Immutability,Mutability,我正在读的一本书说,它不是不变的,因为值是引用类型。除了在类内部创建一个主方法之外,我看不到任何方法可以改变值。它是可变的,因为您可以通过公开的方法更改值的内容 public class A { private int[] values; public int[] getValues() { return values; } } 这意味着您的values属性现在包含已在外部修改的信息。要使其不可变,应返回数组的副本,以确保没有外部代理可以修改它,并应将

我正在读的一本书说,它不是不变的,因为值是引用类型。除了在类内部创建一个主方法之外,我看不到任何方法可以改变值。

它是可变的,因为您可以通过公开的方法更改
值的内容

public class A {
    private int[] values;

    public int[] getValues() {
        return values;
    }
}
这意味着您的
values
属性现在包含已在外部修改的信息。要使其不可变,应返回数组的副本,以确保没有外部代理可以修改它,并应将其设置为最终版本,以便不能创建任何子类来公开状态并使其再次可变:

A obj = new A(...) // values gets filled here
int[] leak = obj.getValues()
leak[0] = 42

您可以获取引用并更改元素数组:

public final class A {
  private int[] values;

  public int[] getValues() {
    return Arrays.copyOf(values);
  }
}
您可以使用反射来更改字段中的值,或者在其他地方(可能是作者的意思),或者使用继承,如下所示

比如说,

不可变类:

不可变对象:

不可变的类和对象:


它实际上不是通过
数组可变的,正如其他答案中所述:该数组是
null
,因为它从未初始化过,并且不能通过getter的结果使其非null

它是可变的,因为您可以将其子类化,并添加可变状态:

import java.util.Arrays;

public final class A {
    private final int[] values;

    public A(int[] values) {
        this.values = null == values ? null : Arrays.copyOf(values);
    }

    public int[] getValues() {
        return Arrays.copyOf(values);
    }
}

请注意,Oracle Java教程中有一个数组,以及有效Java第二版第15项“最小化可变性”中不可变对象所需属性的列表。数组是一种引用类型。你可以找到更多关于参考资料的信息。这意味着当
getValues()
返回
values
时,它实际上返回了一个指向它的指针(由于Java没有显式指针,所以每次都会被取消引用)。虽然由于
是私有的,因此无法重新分配引用本身,但可以更改内容


因此,类似于
a.getValues()[0]++
的东西会增加
values
的第一个元素,假设它不是空的。

如何为空数组的元素赋值?我的意思是
values
不是最终的,因此引用将以
null
开始,但是可以在类的另一部分设置为任何值。但是在这种情况下,我们可以通过反射轻松更改值,不是吗?我认为我们应该划分不可变类和不可变对象,因为这是不可变类,但它不是不可变对象,因为值不是最终值。即使作者假设我们在其他地方更改了值,也意味着我们有了方法或构造函数。如果它是构造函数,则可能没问题(生成了值,但仍然是..反射),如果它是方法,我们可以很容易地更改它。@Egorlitvineko您的“不可变”版本以什么方式不受反射修改的影响?@AndyTurner当您在JVM中启用SecurityManager时,您不能使用反射更改最终字段。你还知道别的吗?
int[]数组=(新的A()).getValues();数组=新整数[5]()数组本身也是可变的…@genemauth。那么
数组的值是多少呢?
?从技术上讲,这是正确的。但在任何现实世界的程序中,
可能会由调用方可以修改的一些数据填充。所以,是的,这个类是不可变的(前提是你把它设为final),但实际上它并不是设计成不可变的。为什么你要创建另一个类并假设它仍然是不可变的呢?@而且我不知道ideone.com默认关闭了断言。我也不知道我的本地IDE在Java项目调试启动时也默认关闭了断言。我站在那里,老师。这不是一成不变的。你需要在ctor中获取一份
值的防御性副本
,并使课程成为最终课程。是的,你是对的,我更正了。
import java.util.Arrays;

public final class A {
    private int[] values;

    public int[] getValues() {
        return values;
    }
}
import java.util.Arrays;

public class A {
    private final int[] values;

    public A(int[] values) {
        this.values = null == values ? null : Arrays.copyOf(values);
    }

    public int[] getValues() {
        return Arrays.copyOf(values);
    }
}
import java.util.Arrays;

public final class A {
    private final int[] values;

    public A(int[] values) {
        this.values = null == values ? null : Arrays.copyOf(values);
    }

    public int[] getValues() {
        return Arrays.copyOf(values);
    }
}
class AA extends A {
  String foo;
}

AA aa = new AA();
aa.foo = "foo";
A a = aa;
aa.foo = "bar";  // Mutates the state of a.