位置:首页 » 文章/教程分享 » Java克隆对象

在这篇文章中,将学习java克隆对象或java中的克隆。 Java Object类附带了原生clone()方法,该方法返回现有实例的副本。

要使用java对象克隆方法,必须实现接口java.lang.Cloneable,以便它不会在运行时抛出CloneNotSupportedException。对象克隆也是受保护的方法,因此要重写它以与其他类一起使用。

下面来看一个例子。

package com.test.cloning;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class Employee implements Cloneable {

    private int id;

    private String name;

    private Map props;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Map getProps() {
        return props;
    }

    public void setProps(Map p) {
        this.props = p;
    }

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

}

请注意,这里使用Object的clone()实现,因此必须要实现Cloneable接口。用一个简单的程序测试clone对象示例。

package com.test.cloning;

import java.util.HashMap;
import java.util.Map;

public class CloningTest {

    public static void main(String[] args) throws CloneNotSupportedException {

        Employee emp = new Employee();

        emp.setId(1);
        emp.setName("Pankaj");
        Map props = new HashMap<>();
        props.put("salary" "10000");
        props.put("city" "Bangalore");
        emp.setProps(props);

        Employee clonedEmp = (Employee) emp.clone();

        // Check whether the emp and clonedEmp attributes are same or different
        System.out.println("emp and clonedEmp == test: " + (emp == clonedEmp));

        System.out.println("emp and clonedEmp HashMap == test: " + (emp.getProps() == clonedEmp.getProps()));

        // Lets see the effect of using default cloning

        // change emp props
        emp.getProps().put("title" "CEO");
        emp.getProps().put("city" "New York");
        System.out.println("clonedEmp props:" + clonedEmp.getProps());

        // change emp name
        emp.setName("new");
        System.out.println("clonedEmp name:" + clonedEmp.getName());

    }

}

上面的克隆示例将产生以下输出:

emp and clonedEmp == test: false
emp and clonedEmp HashMap == test: true
clonedEmp props:{city=New York salary=10000 title=CEO}
clonedEmp name:Pankaj

如果Employee类不实现Cloneable接口,则上面的程序将抛出java.lang.CloneNotSupportedException运行时异常。

Exception in thread "main" java.lang.CloneNotSupportedException: com.test.cloning.Employee
    at java.lang.Object.clone(Native Method)
    at com.journaldev.cloning.Employee.clone(Employee.java:41)
    at com.journaldev.cloning.CloningTest.main(CloningTest.java:19)

下面来看看第一个输出,并了解Object的clone()方法发生了什么,以及是否存在问题。

  1. emp and clonedEmp == test: false,因此emp和clonedEmp是两个不同的对象,而不是指同一个对象。 这符合java克隆对象的要求。

  2. emp and clonedEmp HashMap == test: true,因此emp和clonedEmp对象变量都引用同一个对象。 这带来了克隆的严重问题,接下来我们将看到。

  3. clonedEmp props:{city=New York salary=10000 title=CEO},请注意,没有对clonedEmp属性进行任何更改,但它们仍然被更改,因为emp和clonedEmp变量都引用了同一个对象。 这是一个严重的问题,因为java中的默认克隆不会创建完全分离的对象。 这可能会导致不需要的结果,因此需要正确覆盖java克隆对象方法。

  4. clonedEmp name:Pankaj,这里发生了什么? 我们更改了emp名称,但是clonedEmp名称没有更改。这是因为String是不可变的。 因此,当设置emp名称时,将创建一个新字符串,并在this.name = name;中更改emp名称引用。 因此clonedEmp名称保持不变。 也会发现任何原始变量类型的类似行为。 因此,只要在对象中只有原始和不可变变量,就可以使用java clone对象的默认方法。

浅克隆
java clone对象的默认实现是使用浅拷贝,类似下面使用反射。

@Override
 public Object clone() throws CloneNotSupportedException {

     Employee e = new Employee();
     e.setId(this.id);
     e.setName(this.name);
     e.setProps(this.props);
     return e;
}

深度克隆
在深度克隆中,必须逐个复制字段。可以覆盖下面的克隆方法进行深度克隆。