在Java中,正在传递一个对象';作为对象句柄传递的方法的非原语包含字段,如果是,如何影响其易变性?
如果对象的非原语包含字段作为引用该字段对象的对象句柄传递,那么如果更新/更改最初传递的字段,该字段是否容易在事后被更改在Java中,正在传递一个对象';作为对象句柄传递的方法的非原语包含字段,如果是,如何影响其易变性?,java,pass-by-reference,immutability,pass-by-value,mutability,Java,Pass By Reference,Immutability,Pass By Value,Mutability,如果对象的非原语包含字段作为引用该字段对象的对象句柄传递,那么如果更新/更改最初传递的字段,该字段是否容易在事后被更改 公共类可变狗 { 公共字符串名称; 公共字符串颜色; 公共可变狗(字符串名称、字符串颜色) { this.name=名称; 这个颜色=颜色; } } 公共类ImmutableDog//这些对象的字段真的不会更改吗? { 私有最终字符串名; 私人最终字符串颜色; 公共免疫犬(可变犬) { this.name=doggy.name; this.color=doggy.color;
公共类可变狗
{
公共字符串名称;
公共字符串颜色;
公共可变狗(字符串名称、字符串颜色)
{
this.name=名称;
这个颜色=颜色;
}
}
公共类ImmutableDog//这些对象的字段真的不会更改吗?
{
私有最终字符串名;
私人最终字符串颜色;
公共免疫犬(可变犬)
{
this.name=doggy.name;
this.color=doggy.color;
}
公共字符串getColor()
{
返回此.color;
}
}
公共静态void main(字符串[]args)
{
易变狗=新易变狗(“勇气”,“粉色”);
ImmutableDog anImmutableDog=新的ImmutableDog(aMutableDog);
aMutableDog.color=“粉色/黑色”;
anImmutableDog.getColor().equals(aMutableDog.color);//对还是错?
}
本质上,ImmutableDog
真的是不可变的吗?在本例中,使用了字符串。使用易变对象(如集合
)会有所不同吗
这个问题是对答案的回应。
ImmutableDog
确实是不可变的,即使它可以从可变对象接收字符串。这是因为String
s是不可变的。这也证明了不变性的一大好处——你可以传递不可变的对象,而不用担心它们会突然改变
您可能认为,ImmutableDog
中的字段可以通过设置MutableDog
实例的字段来进行更改:
aMutableDog.color = "Pink/Black";
但是,“Pink/Black”
与此处分配给ImmutableDog
的字符串实例不同,因此ImmutableDog
不会更改
另一方面,如果
ImmutableDog
有一个可变类型的字段,那么它就不再是真正不可变的了
例如,这是相同的代码,但使用StringBuilder
:
public class MutableDog
{
public StringBuilder name;
public StringBuilder color;
public MutableDog(StringBuilder name, StringBuilder color)
{
this.name = name;
this.color = color;
}
}
public class ImmutableDog // are fields of these objects truly safe from changing?
{
private final StringBuilder name;
private final StringBuilder color;
public ImmutableDog(MutableDog doggy)
{
this.name = doggy.name;
this.color = doggy.color;
}
public String getColor()
{
return this.color.toString();
}
}
public static void main(String[] args)
{
MutableDog aMutableDog = new MutableDog("Courage", "Pink");
ImmutableDog anImmutableDog = new ImmutableDog(aMutableDog);
aMutableDog.color.append(" and Black");
anImmutableDog.getColor().equals(aMutableDog.color);
}
现在不变的狗的颜色将出现变化。您仍然可以通过在构造函数中复制字符串生成器来防止这种情况:
public ImmutableDog(MutableDog doggy)
{
this.name = new StringBuilder(doggy.name);
this.color = new StringBuilder(doggy.color);
}
但是,这仍然允许您(意外地)在ImmutableDog
类中修改字符串生成器
所以不要将可变类存储在不可变类中。:) 非常简单:java中的任何引用类型最终都指向某个对象 如果该对象具有可变状态,那么对它的任何引用都可以用来更改该状态 因此,您是正确的:仅在每个字段声明之前放置
private final
,并不一定会使该类本身不可变
在您的示例中,String类是不可变的(除了使用不安全的非常模糊的技巧之外)。在分配了名称
和颜色
之后,引用不能更改,它们指向的对象也不能更改
但是当然:例如,如果类型是List
,那么底层对象很可能在其他地方发生了更改。如果你想防止这种情况发生,你必须创建一个传入列表的副本,并保留对它的引用。它真的在所有意义和情况下都是不变的吗否。它在某种程度上是不变的吗是。
只要你只做变量赋值,不变的狗实际上总是保持不变。这通常是由于传递值语义造成的,这一点已被详细解释
在ImmutableDog
dog中复制color
的引用时,实际上是复制句柄。然后,当您通过为可变狗指定粉色/黑色
值来修改颜色时,可变狗的句柄会发生变化,但不可变狗仍然保持原始句柄,指向原始颜色
对于String
类型,这更进一步,因为字符串是不可变的。因此,对字符串调用任何方法都保证不会修改原始值。所以就字符串而言,是的,不可变的狗是真正不可变的,可以信任
集合确实起到了作用。如果我们稍微更改了类的设计:
public class MutableDog {
public String name;
public List<String> acceptedMeals;
public MutableDog(String name, List<String> acceptedMeals) {
this.name = name;
this.acceptedMeals = new ArrayList<>(acceptedMeals);
}
}
public class ImmutableDog {
private final String name;
private final Iterable<String> acceptedMeals;
public ImmutableDog(MutableDog doggy) {
this.name = doggy.name;
this.acceptedMeals = doggy.acceptedMeals;
}
public Iterable<String> getAcceptedMeals() {
return this.acceptedMeals;
}
}
现印备如下:
Chicken
Chicken
Pasta
这是由于ImmutableDog
的构造函数将句柄复制到acceptedMeals
集合,但新句柄指向与原始集合相同的位置。因此,当您通过可变狗调用修改时,由于ImmutableDog
指向内存中的同一位置,因此其值也可能会被修改
在这种特定情况下,您可以通过执行集合的深度复制而不是简单地复制引用句柄来避免这种副作用:
public ImmutableDog(MutableDog doggy) {
this.name = doggy.name;
this.acceptedMeals = new ArrayList<>(doggy.acceptedMeals);
}
字符串文本总是存储在内存中,因为“字符串”数据类型在java中是不可变的。因此,nImmutableDog.getColor().equals(aMutableDog.color)
的输出是false
。但是,如果我们有可变类而不是字符串,那么它也会改变,因为它是通过引用传递的。因此,如果我理解正确,如果MutableDog
字段在用于实例化ImmutableDog
字段后被重新分配,这不会更改ImmutableDog
字段所指向的原始字符串。但是,如果ImmutableDog
字段所指向的原始字符串发生了更改(这在现实中是不可能的,但假设是),那么该更改将反映在ImmutableDog
字段中?整个Java都是“按值传递”…除了对象
public ImmutableDog(MutableDog doggy) {
this.name = doggy.name;
this.acceptedMeals = new ArrayList<>(doggy.acceptedMeals);
}
Chicken
Chicken