使用可变字段进行对象克隆

时间:2014-10-19 07:25:50

标签: java

我正在研究clone()课程的Object方法。我试过以下例子。

public class Orange {
  private Double price;

  public Double getPrice() {
    return price;
  }  

  public void setPrice(Double price) {
    this.price = price;
  }
}

public class Fruit implements Cloneable {
  private String colour;
  private String fruitCode;
  private Orange orange;

  public String getColour() {
    return colour;
  }

  public void setColour(String colour) {
    this.colour = colour;
  }

  public String getFruitCode() {
    return fruitCode;
  }

  public void setFruitCode(String fruitCode) {
    this.fruitCode = fruitCode;
  }

  public Orange getOrange() {
    return orange;
  }

  public void setOrange(Orange orange) {
    this.orange = orange;
  }

  @Override
  protected Object clone() throws CloneNotSupportedException {
    return super.clone();
  }

  @Override
  public boolean equals(Object obj) {
    Fruit other = (Fruit)obj;
    return new EqualsBuilder().append(this.getFruitCode(), other.getFruitCode()).append(this.colour, other.colour).isEquals();
  }

  @Override
  public int hashCode() {
    return new HashCodeBuilder().append(this.getFruitCode()).append(this.colour).toHashCode();
  }
}
public class CloneTest {
  public static void main(String[] args) {
    Fruit fruit = new Fruit();
    try {
        fruit.setOrange(new Orange());
        fruit.getOrange().setPrice(12.45);
        fruit.setFruitCode("X1");

        Fruit clonedFruit = (Fruit) fruit.clone();

        fruit.setFruitCode("X2");

        fruit.getOrange().setPrice(15.5);

        System.out.println("fruit's orange price is : "+fruit.getOrange().getPrice());
        System.out.println("clonedFruit's orange price is : "+clonedFruit.getOrange().getPrice());

        System.out.println("fruit's code is : "+fruit.getFruitCode());
        System.out.println("fruit's code is : "+clonedFruit.getFruitCode());

    } catch (CloneNotSupportedException ex) {
        Logger.getLogger(CloneTest.class.getName()).log(Level.SEVERE, null, ex);
    }
  }
}

                    **out put is** 
                    fruit's orange price is : 15.5
                    clonedFruit's orange price is : 15.5
                    fruit's code is : X2
                    fruit's code is : X1

请注意,我在克隆对象后更改了orange对象的价格字段和水果的fruitCode字段。但我已经通知了一个问题,即我为橙色对象的价格字段所做的更改已经受到了对象的影响,这些对象是原始对象和克隆对象。 但果实的水果代码领域并没有发生这种情况

而且当我把下面一行显示为打印为真时。

System.out.println(fruit.getOrange()== clonedFruit.getOrange()); 

我有点困惑,觉得clonedFruit和水果参考都指向同一个橙色物体。当我搜索在线文章时,我注意到了可变对象的克隆问题。 但对我来说不够明确。 任何人都可以清楚地向我解释它是如何发生的,我该怎么做才能阻止这个

2 个答案:

答案 0 :(得分:3)

可悲的是,默认的object.clone()方法并不总是产生一个实际的克隆。克隆基本变量时,复制一个值,但是,如果克隆一个对象,它会将地址复制到该对象而不是它的值。

这就是为什么你通常应该为你的类定义你自己的克隆方法(可能有一些语法错误,但逻辑就在那里。

  @Override
  protected Object clone() throws CloneNotSupportedException {
     Orange orange = new Orange();
     //copy every basic variable
     orange.setPrice(price);

     //clone any Objects that have clone() method defined;
     orange.setImage(this.Image.clone()); //just an example because you don't have 1 in your code
     // for any objects that do not have clone() defined you have to copy each field manually
     GeneticStructure gn = new GeneticStructure();
     gn.setGeneticCode( this.geneticStructure.getGeneticCode());
     gn.setName( this.geneticStructure.getGeneticStructire());
     orange.setGeneticStructure( gn )

     return orange;
  }

答案 1 :(得分:0)

默认情况下, clone()方法始终返回要克隆的对象的浅层副本。这意味着它将复制基本类型变量的值并复制引用类型变量的引用。

让我们举个例如你的Fruit课程:

注意:您的Fruit类不一定需要实现Cloneable接口,因为它已经由Object实现,它始终是任何Java类的超类。

public class Fruit implements Cloneable {
  private String colour;
  private String fruitCode;
  private Orange orange;
  // Remaining methods
}

现在,当您创建Friut类的对象并将橙色引用变量初始化为:

Fruit fruit = new Fruit();
fruit.setOrange(new Orange());

然后,创建一个新的 Orang e类对象,并将其引用分配给 Fruit 对象的变量 orange

但是,当你打电话时:

Fruit clonedFruit = (Fruit)fruit.clone();

然后,内部不会创建新的橙色对象,而是将以上创建的橙色对象引用提供给引用变量指向它。这就是为什么你得到 true 的原因:

System.out.println(fruit.getOrange()== clonedFruit.getOrange());  // true

但是,肯定会创建一个新的Fruit对象。 所以,当你致电:

fruit.getOrange().setPrice(12.5);

然后

fruit.getOrange().setPrice(15.5);

后者将基本上覆盖先前的值,并且对象即fruit和clonedFruit都可以看到此修改,因为两者都包含对同一Orange对象的引用。 但是,当你打电话时:

fruit.setFruitCode("X1");
Fruit clonedFruit = (Fruit) fruit.clone();

然后,创建一个新的Fruit类对象,此处的 clonedFruit 对象仅包含fruitCode值为“X1”, 以后你打电话的时候:

fruit.setFruitCode("X2");

然后只会影响 fruit 对象的 fruitCode 值,该值现在将更改为“X2”。但这不会影响clonedFruit的 fruitCode 值,因为 fruit clonedFruit 是两个不同的对象