在这篇文章中,将学习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()方法发生了什么,以及是否存在问题。
-
emp and clonedEmp == test: false,因此emp和clonedEmp是两个不同的对象,而不是指同一个对象。 这符合java克隆对象的要求。
-
emp and clonedEmp HashMap == test: true,因此emp和clonedEmp对象变量都引用同一个对象。 这带来了克隆的严重问题,接下来我们将看到。
-
clonedEmp props:{city=New York salary=10000 title=CEO},请注意,没有对clonedEmp属性进行任何更改,但它们仍然被更改,因为emp和clonedEmp变量都引用了同一个对象。 这是一个严重的问题,因为java中的默认克隆不会创建完全分离的对象。 这可能会导致不需要的结果,因此需要正确覆盖java克隆对象方法。
-
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;
}
深度克隆
在深度克隆中,必须逐个复制字段。可以覆盖下面的克隆方法进行深度克隆。