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.