首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >浅克隆和深克隆

浅克隆和深克隆
EN

Stack Overflow用户
提问于 2016-08-31 15:12:54
回答 3查看 290关注 0票数 0

我正在回顾Java的一些基本方面,并对浅克隆和深克隆有一些疑问。我知道它们的细节,并了解它们的内部结构。但我偶然发现了这个简单的练习-

部门类-

代码语言:javascript
复制
public class Department {
    String deptName;

    public String getdName() {
        return deptName;
    }

    public void setdName(String dName) {
        this.deptName = dName;
    }

    public Department() {
        super();
    }

    public Department(String dName) {
        super();
        this.deptName = dName;
    }
}

员工类-

代码语言:javascript
复制
public class Employee implements Cloneable{
    int empNo;
    String empName;
    Department dept;

    public int getEmpNo() {
        return empNo;
    }
    public void setEmpNo(int empNo) {
        this.empNo = empNo;
    }
    public String getEmpName() {
        return empName;
    }
    public void setEmpName(String empName) {
        this.empName = empName;
    }
    public Department getDept() {
        return dept;
    }
    public void setDept(Department dept) {
        this.dept = dept;
    }
    public Employee(int empNo, String empName, Department dept) {
        super();
        this.empNo = empNo;
        this.empName = empName;
        this.dept = dept;
    }
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

主类-

代码语言:javascript
复制
public class ShalowCopyTest {

    public static void main(String args[]) {
        Department dept1 = new Department("Development");
        Department dept2 = new Department("Testing");
        Employee emp1 = new Employee(10, "Peter", dept1);
        Employee emp2 = null;
        try {
            emp2 = (Employee) emp1.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        System.out.println(emp1.getDept().getdName());
        System.out.println(emp2.getDept().getdName());

        System.out.println("Now changing emp1.dept");
        //emp1.setDept(dept2); //This is deep cloning - why?
        emp1.getDept().setdName("Testing"); //This is shallow cloning
        System.out.println(emp1.getDept().getdName());
        System.out.println(emp2.getDept().getdName());
    }
}

您可以看到,如果我使用emp1.setDept(dept2),更改emp1的部门对emp2没有任何影响。这就是深度克隆。但是,仅通过emp1.getDept().setdName(“测试”)更改dName也会导致emp2的dName更改。所以这是浅层克隆。为什么这两行代码会有所不同呢?为什么它们是不一样的?谢谢。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2016-08-31 17:31:15

我注意到您使用的示例与此链接中的示例相同:A Guide to Object Cloning in Java

您可能已经知道,super.clone()的默认实现执行浅复制,而不是深复制。

在本例中,两个对象实例: emp1和emp2都将包含指向内存中相同位置的引用。引用部门实例,这是克隆emp1对象后它在内存中引用的方式:

两者都引用内存中的同一位置,其中包含一个不可变的字符串:"Development“。

通过设置:

代码语言:javascript
复制
emp1.setDept(dept2);

它将emp1的引用更改为新的对象dept2:

在这种情况下,您会将其视为Deep Copy,但实际上并非如此。只是emp1将它的引用更改为内存中的一个新位置。

在另一种情况下,当您设置:

代码语言:javascript
复制
emp1.getDept().setdName("Testing");

您将Departman的名称更改为"Testing“-这对两个对象实例都是可见的。

如果你还有什么问题,请告诉我。

票数 1
EN

Stack Overflow用户

发布于 2016-08-31 16:56:54

你的例子是一个浅的,现在是一个深的副本,它只是看起来你有一个深的副本。让我来解释一下原因:

您要克隆的对象(Employee)确实具有复杂数据类型(Department)。如果克隆包含其他对象的对象,则只复制引用,而不复制实际对象。

让我们来看看你的代码:

代码语言:javascript
复制
emp1.setDept(dept2)

在本例中,您不会更改实际实例(这会对克隆和原始实例产生影响),但会将一个新对象分配给emp1dept实例。因此,您现在在emp1emp2中有不同的dept实例。这就是为什么看起来你有一个很深的副本。

假设您的Department中有另一个实例变量,其中包含所有必要的方法:

代码语言:javascript
复制
public class Department {
    String deptName;
    int someNumber;

    public void setNumber(int number){
    someNumber=number;
    }

    public int getNumber(){
    return someNumber
    }


    public Department(String dName, int number){
    super();
    deptName = dName;
    someNumber = number;
    }
    }

在你的main方法中,你可以这样做:

代码语言:javascript
复制
Department dept1 = new Department(“Development”, 50);
Employee emp1 = new Employee(10, “Peter”, dept1);
Employee emp2 = null;
emp2 = (Employee) emp1.clone();
System.out.println(emp1.getDept().getNumber; // Output: 50
System.out.println(emp1.getDept().getNumber; // Output: 50
//Now we change the instance variable of dep1 inside emp1
emp1.getDept().setNumber(100);
//Now print the numbers of both employees again
System.out.println(emp1.getDept().getNumber; // Output: 100
System.out.println(emp1.getDept().getNumber; // Output: 100

正如你所看到的,这是一个浅的拷贝,而不是深的拷贝。emp1.getDept().setdName(“Testing”)只是改变原始的而不是克隆的唯一原因是因为字符串是不可变的。每次更改String时,您都会得到一个新的String实例,因此在克隆对象后,它不会对原始的String实例产生任何影响。

要使您的示例成为深度副本,您必须像这样调整您的clone方法:(我还做了一些小调整,以便数据类型是匹配的,您不再需要try/catch )

代码语言:javascript
复制
@Override
public Employee clone(){
Employee clone = null;

try{
clone = (Employee)super.clone();
clone.Department = Department.clone(); // This is the important line!! You need to clone the Department Object aswell. 
}catch (CloneNotSupportedException e){
e.printStackTrace();
    }
return clone;
}

当然,如果您的Department类也要使其正常工作,则必须在内部实现clonable接口和clone方法。

票数 1
EN

Stack Overflow用户

发布于 2016-08-31 17:22:35

由于您使用的是super.clone(),而super是一个Object,因此您会得到一个浅克隆,其中每个字段都会被复制。

因此,如果您从以下内容开始:

emp1 -> dept1 -> dname1

然后是emp2 = emp2.clone(),你会得到:

emp1 -> dept1 -> dname1 ^ emp2 -/

即它们都指向相同的Department对象。

如果您随后执行dept1.name = dname2,则只会影响dept1,因此您会得到:

emp1 -> dept1 -> dname2 ^ emp2 -/

..。emp1.getDepartment().name = dname2也有完全相同的效果--不管你是如何获得对象的。

如果您现在执行emp2.department = dept2,它不会影响emp1,因此您最终会得到:

emp1 -> dept1 -> ... //未更改的emp2 -> dept2 -> ... //新分配

要获得深度克隆,您需要编写自己的克隆例程,该例程克隆每个级别。

这很容易出错。最好养成使用不可变对象的习惯,在这种对象中,浅拷贝“就行了”--因为您永远不能更改字段,所以您不可能意外地影响与您正在使用的对象共享同一对象的对象。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39243014

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档