为什么Object类的clone()方法给出了对象的深层副本?

时间:2014-04-05 06:16:31

标签: java clone deep-copy shallow-copy

根据JAVA文档,super.clone()在调用时返回对象的浅表副本。在下面的代码中,我有两个对象名称 id ;和一个原始变量 num 。 当在第一个对象上调用super.clone()方法时,除了只有num的预期副本之外,它似乎还创建了对象的深层副本(name和id)。克隆对象obj后,我更改了它的名称和id字段。如果正在制作浅拷贝,则这些更改应反映在克隆对象中。我是对的吗?

public class Cloning implements Cloneable {
String name;
int num;
Integer id;
Cloning(String name,int num,Integer id)
{
    this.name = name;
    this.num = num;
    this.id = id;
}
public Object clone()
{
    try
    {
        return super.clone();
    }
    catch(CloneNotSupportedException E)
    {
        System.out.println(E.getMessage());
        return null;
    }
}
public void print()
{
    System.out.println(name);
    System.out.println(num);
    System.out.println(id);
}


public static void main(String[] args) {
    // TODO Auto-generated method stub
    Cloning obj = new Cloning("Annu",203,new Integer(3));
    Cloning obj1 = (Cloning)obj.clone();
    obj.name = "Annu_modified";
    obj.num = 204;
    obj.id = new Integer(4);
    obj.print();
    obj1.print();


 }
}
  

我在运行代码时得到以下输出:

Annu_modified

204

4

Annu

203

3

4 个答案:

答案 0 :(得分:1)

name和id字段是对String和Integer类型的对象的引用。当您进行浅拷贝时,新副本指向名称和id的相同对象。

然后当你做

obj.name =“Annu_modified”;

更改obj.name以引用String类型的新对象,而obj1.name继续引用旧对象。如果您可以更改对象obj.name,则它将更改为两者。但是对于String,你不能使它成为一个所谓的不可变对象。

答案 1 :(得分:0)

尝试此测试

    Cloning obj = new Cloning("Annu",203, 1000);
    Cloning obj1 = (Cloning)obj.clone();
    System.out.println(obj.id == obj1.id);

它打印true,这意味着克隆对象的id指向同一个Integer实例,如果它是深度克隆则会打印false

答案 2 :(得分:0)

class Subject {

  private String name;

  public String getName() {
    return name;
  }

  public void setName(String s) {
    name = s;
  }

  public Subject(String s) {
    name = s;
  }
}

class Student implements Cloneable {
  //Contained object
  private Subject subj;

  private String name;

  public Subject getSubj() {
    return subj;
  }

  public String getName() {
    return name;
  }

  public void setName(String s) {
    name = s;
  }

  public Student(String s, String sub) {
    name = s;
    subj = new Subject(sub);
  }

  public Object clone() {
    //shallow copy
    try {
      return super.clone();
    } catch (CloneNotSupportedException e) {
      return null;
    }
  }
}

public class CopyTest {

  public static void main(String[] args) {
    //Original Object
    Student stud = new Student("John", "Algebra");

    System.out.println("Original Object: " + stud.getName() + " - "
        + stud.getSubj().getName());

    //Clone Object
    Student clonedStud = (Student) stud.clone();

    System.out.println("Cloned Object: " + clonedStud.getName() + " - "
        + clonedStud.getSubj().getName());

    stud.setName("Dan");
    stud.getSubj().setName("Physics");

    System.out.println("Original Object after it is updated: " 
        + stud.getName() + " - " + stud.getSubj().getName());

    System.out.println("Cloned Object after updating original object: "
        + clonedStud.getName() + " - " + clonedStud.getSubj().getName());

  }
}

Output is:
Original Object: John - Algebra
Cloned Object: John - Algebra
Original Object after it is updated: Dan - Physics
Cloned Object after updating original object: John - Physics

在这个例子中,我所做的只是实现你想用Clonable接口复制的类,并覆盖Object类的clone()方法并在其中调用super.clone()。如果您观察到,对" name"所做的更改原始对象(学生类)的字段不会反映在克隆对象中,而是对" name"所做的更改。包含对象(Subject类)的字段反映在克隆对象中。这是因为克隆的对象携带Subject对象的内存地址,但不包含实际值。因此,Original对象中Subject对象的任何更新都将反映在Cloned对象中。

答案 3 :(得分:0)

让我们看看您示例中的一个部分:obj.id = new Integer(4);。在这里,您不会更改id的内部表示 - 您将新实例分配给id引用。 IntegerString都是不可变的,因此很难感觉浅层与深层复制的区别。尝试添加例如一个ArrayList属性,为了修改它,您可以例如添加新元素obj.myList.add(13);