2

我遇到了这个问题,这一直让我发疯。简而言之,我实例化了同一个类的两个对象。当我在一个对象中运行一个方法时,另一个对象也会受到影响,就好像我在第二个对象上显式调用了一个方法一样。我想知道是否有人可以帮我解决这个问题。

假设,我有课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 pinto pCopy

Portfolio pCopy = new Portfolio(p);

所以此时我有两个相同的对象。还有一个是按值复制的对象。更改 中的字段值pCopy不会影响 中的相同字段p

现在,当我在 上运行一个方法applyStressp,仪器列表中的值pCopy也会发生变化。

换句话说,如果p.portfolio.get(0) == 1,那么在 之后p.applyStress,我希望看到p.portfolio.get(0)is2pCopy.portfolio.get(0)is1

但我看到的p.portfolio.get(0)是现在2也是pCopy.portfolio.get(0)2

我不明白为什么会这样。这不是static修饰符问题,因为没有静态修饰符。有人有什么想法吗?

4

2 回答 2

3

应用于您的ArrayList 引用的克隆方法是浅拷贝,而不是深拷贝。这意味着您在原始集合中拥有的任何内容都由克隆的集合共享。

这意味着您还需要克隆每个乐器,或者为每个乐器提供一个复制构造函数。

this.portfolio = new ArrayList<Instrument>();
for(Instrument toBeCopiedInstrument : copyFrom.portfolio){
   this.portfolio.add(new Instrument(toBeCopiedInstrument ));
}
于 2012-05-30T00:18:02.883 回答
1

默认情况下.clone()执行所谓的 a shallow copy,这意味着它只是将 a 复制reference到存在的对象中Listcloned它实际上并没有将对象本身复制到新实例中。

您需要做的是为列表中deep copy包含的List每个项目实现自定义。但是deep clone在 Java 中是一个破碎的概念和实现。

Acopy constructor在 Java 中也不是一个很好的模式,因为在大多数情况下,您最终也会复制引用,并且您注入到构造函数的每个对象都必须在整个链中遵循相同的复制构造函数语义。与 C++ 不同,这是手动、乏味、不可维护且容易出错的过程!

.clone()并且implements Cloneable是在 Java 中获得正确概念的最复杂的部分。在精心设计的应用程序中很少需要它们。也就是说,如果你正在使用.clone()你可能做错了。如果对对象进行按位复制是您设计的一部分,而不是存储,那么您可能需要重新审视您的设计。

乔什·布洛赫谈设计

Object 的 clone 方法非常棘手。它基于现场副本,并且是“语言外的”。它创建一个对象而不调用构造函数。不能保证它保留了构造函数建立的不变量。多年来,无论在 Sun 内部还是外部,都出现了很多错误,这是因为如果您只是在链上反复调用 super.clone 直到您克隆了一个对象,您就会得到该对象的浅表副本。克隆通常与被克隆的对象共享状态。如果该状态是可变的,则您没有两个独立的对象。如果你修改一个,其他的也会改变。突然之间,你得到随机行为。

不可变

一个更好的模式是让一切都不可变。这样您就不需要单独的实例,您可以共享实例直到它们需要更改,然后它们会更改并且您拥有一个包含新数据的新实例,可以共享而没有任何副作用

于 2012-05-30T02:01:47.943 回答