在这篇文章中,将学习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<String, String> 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<String, String> getProps() {
return props;
}
public void setProps(Map<String, String> 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<String, String> 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;
}
深度克隆
在深度克隆中,必须逐个复制字段。可以覆盖下面的克隆方法进行深度克隆。
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/276192.html