在这篇文章中,将学习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/tech/pnotes/264078.html