我遇到了这个问题,这让我疯了。简而言之,我实例化了同一个类的两个对象。当我在一个对象中运行一个方法时,另一个对象也会受到影响,就像我在第二个对象上显式调用了一个方法一样。我想知道是否有人可以请你帮忙。
假设我有课程Portfolio
...
public class Portfolio implements Cloneable {
public ArrayList<Instrument> portfolio;
private String name;
private String description;
public Portfolio() {
portfolio = new ArrayList<Instrument>();
}
public Portfolio(Portfolio copyFrom) {
//attempt to copy the object by value
this.portfolio = (ArrayList<Instrument>) copyFrom.portfolio.clone();
this.name = copyFrom.name;
this.description = copyFrom.description;
}
public void applyStress(Stress stress) {
this.portfolio.set(0,this.portfolio.get(0)+1;
}
第一个构造函数用于实例化一个对象等。第二个构造函数用于按值复制一个对象。
方法applyStress
用于运行求和计算。在我们的例子中,我简化了方法,因此除了为对象中的任何内容添加+1之外什么都不做。
所以我会将对象实例化为
Portfolio p = new Portfolio();
然后我会分配到portfolio
字段,一些工具;
p.portfolio = someInstrumentList;
然后我会将portfolio p
的值复制到pCopy
:
Portfolio pCopy = new Portfolio(p);
所以此时我有两个相同的对象。另外一个是按值复制的对象。更改pCopy
中字段的值不会影响p
中的相同字段。
现在,当我在applyStress
上运行方法p
时,pCopy
中的工具列表值也会发生变化。
换句话说,如果是p.portfolio.get(0) == 1
,那么在p.applyStress
之后,我希望看到p.portfolio.get(0)
是2
而pCopy.portfolio.get(0)
是1
}
但我所看到的是p.portfolio.get(0)
2
而pCopy.portfolio.get(0)
也是2
我不明白为什么会这样。这不是static
修饰符问题,因为没有静态修饰符。有人有什么想法吗?
答案 0 :(得分:3)
您的ArrayList
引用应用于您的克隆方法执行shallow copy,而不是deep copy。这意味着原始集合中的任何内容都由克隆者共享。
这意味着您还需要克隆每个乐器,或者为每个乐器提供一个复制构造函数。
this.portfolio = new ArrayList<Instrument>();
for(Instrument toBeCopiedInstrument : copyFrom.portfolio){
this.portfolio.add(new Instrument(toBeCopiedInstrument ));
}
答案 1 :(得分:1)
默认.clone()
执行所谓的shallow copy
,这意味着它只会将reference
复制到List
中cloned
所保留的对象deep copy
1}},它实际上并没有将对象本身复制到新实例。
您需要为List
和列表中包含的每个项目实施自定义deep clone
。但copy constructor
在Java中是一个破碎的概念和实现。
.clone()
在Java中也不是一个非常好的模式,因为在大多数情况下你最终也会复制引用,并且你注入构造函数的每个对象都必须遵循相同的复制构造函数语义沿着链子。与C ++不同,这是一个手动,繁琐,不可维护且容易出错的过程!
implements Cloneable
和{{1}}是Java中获取正确概念的最复杂的一部分。在精心设计的应用程序中很少需要它们。 That is, if you are using .clone()
you are probably doing it wrong。如果您的对象的位副本是您的设计的一部分,而不是存储,您可能需要重新审视您的设计。
对象的克隆方法非常棘手。它基于现场副本,和 这是“超语言”。它创建一个对象而不调用a 构造函数。无法保证它保留不变量 由建设者建立。有很多错误 多年来,无论是在孙内外,都源于如果你这样的事实 只需要反复调用super.clone直到你克隆了一个 对象,你有一个对象的浅表副本。克隆一般 与正在克隆的对象共享状态。如果那个状态是可变的, 你没有两个独立的对象。如果你修改一个,另一个 变化也是如此。突然之间,你会得到随机行为。
<强>不可变强>
更好的模式是让一切都变得不可变。这样您就不需要单独的实例,您可以共享实例,直到他们需要更改,然后他们更改,您有一个新数据的实例,可以在没有任何副作用的情况下共享。