java中clone方法
Cloning is the process of creating a copy of an Object. Java Object class comes with native clone()
method that returns the copy of the existing instance.
克隆是创建对象副本的过程。 Java Object类带有本机clone()
方法,该方法返回现有实例的副本。
Since Object is the base class in Java, all objects by default support cloning.
由于Object是Java中的基类,因此默认情况下所有对象都支持克隆。
Java对象克隆 (Java Object Cloning)
If you want to use Java Object clone() method, you have to implement the java.lang.Cloneable
marker interface. Otherwise, it will throw CloneNotSupportedException
at runtime.
如果要使用Java Object clone()方法,则必须实现java.lang.Cloneable
标记接口。 否则,它将在运行时引发CloneNotSupportedException
。
Also Object clone is a protected method, so you will have to override it.
Let’s look at Object cloning in Java with an example program.
让我们来看一个示例程序中的Java对象克隆。
package com.journaldev.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();
}
}
We are using Object clone() method, so we have implemented the Cloneable
interface. We are calling the superclass clone() method i.e. Object clone() method.
我们使用的是Object clone()方法,因此我们实现了Cloneable
接口 。 我们正在调用超类clone()方法,即Object clone()方法。
使用对象clone()方法 (Using Object clone() Method)
Let’s create a test program to use the object clone() method to create a copy of the instance.
让我们创建一个测试程序,以使用对象clone()方法创建实例的副本。
package com.journaldev.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()));
// Let's 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());
}
}
Output:
输出:
emp and clonedEmp == test: false
emp and clonedEmp HashMap == test: true
clonedEmp props:{city=New York, salary=10000, title=CEO}
clonedEmp name:Pankaj
运行时发生CloneNotSupportedException (CloneNotSupportedException at Runtime)
If our Employee class won’t implement Cloneable
interface, the above program will throw CloneNotSupportedException
runtime exception.
如果我们的Employee类不会实现Cloneable
接口,则上述程序将抛出CloneNotSupportedException
运行时异常 。
Exception in thread "main" java.lang.CloneNotSupportedException: com.journaldev.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)
了解对象克隆 (Understanding Object Cloning)
Let’s look into the above output and understand what’s happening with Object clone()
method.
让我们看一下上面的输出,并了解Object clone()
方法的情况。
emp and clonedEmp == test: false
: It means that emp and clonedEmp are two different objects, not referring to the same object. This is in agreement with the java object cloning requirement.emp and clonedEmp == test: false
:这意味着emp和clonedEmp是两个不同的对象,而不是引用同一对象。 这与Java对象克隆要求一致。emp and clonedEmp HashMap == test: true
: So both emp and clonedEmp object variables refer to the same object. This can be a serious data integrity issue if we change the underlying object value. Any change in the value might get reflected to the cloned instance too.emp and clonedEmp HashMap == test: true
:因此emp和clonedEmp对象变量都引用同一对象。 如果我们更改基础对象值,这可能是一个严重的数据完整性问题。 值的任何更改也可能会反映到克隆的实例中。clonedEmp props:{city=New York, salary=10000, title=CEO}
: We didn’t make any change in clonedEmp properties, but still, they got changed because both emp and clonedEmp variables are referring to the same object.This is happening because the default Object clone() method creates a shallow copy. It can be a problem when you want to create totally detached objects through cloning process. This can lead to unwanted results, hence the need to properly override the Object clone() method.clonedEmp props:{city=New York, salary=10000, title=CEO}
:我们没有对clonedEmp属性进行任何更改,但是仍然更改了它们,因为emp和clonedEmp变量都指向同一个对象。这是发生这种情况是因为默认的Object clone()方法创建了一个浅表副本。 当您要通过克隆过程创建完全分离的对象时,可能会出现问题。 这可能会导致不想要的结果,因此需要正确地重写Object clone()方法。clonedEmp name:Pankaj
: What happened here?We changed the emp name but clonedEmp name didn’t change. It’s because String is immutable. So when we are setting emp name, a new string is created and emp name reference is changed inthis.name = name;
.Hence clonedEmp name remains unchanged. You will find similar behavior for any primitive variable types too. So we are good with java object default cloning as long as we have only primitive and immutable variables in the object.clonedEmp name:Pankaj
:这里发生了什么?我们更改了emp名称,但clonedEmp名称未更改。 这是因为String是不可变的 。 因此,当我们设置emp名称时,将创建一个新字符串,并在this.name = name;
更改emp名称引用this.name = name;
。因此clonedEmp名称保持不变。 对于任何原始变量类型,您也会发现类似的行为。 因此,只要对象中只有原始变量和不可变变量,我们就擅长使用Java对象默认克隆。
对象克隆类型 (Object Cloning Types)
There are two types of object cloning – shallow cloning, and deep cloning. Let’s understand each of them and find out the best way to implement cloning in our Java programs.
有两种类型的对象克隆–浅克隆和深克隆。 让我们理解它们中的每一个,并找出在我们的Java程序中实现克隆的最佳方法。
1.浅克隆 (1. Shallow Cloning)
The default implementation of Java Object clone() method is using shallow copy. It’s using reflection API to create the copy of the instance. The below code snippet showcase the shallow cloning implementation.
Java Object clone()方法的默认实现是使用浅表复制。 它使用反射 API创建实例的副本。 下面的代码片段展示了浅层克隆实现。
@Override
public Object clone() throws CloneNotSupportedException {
Employee e = new Employee();
e.setId(this.id);
e.setName(this.name);
e.setProps(this.props);
return e;
}
2.深克隆 (2. Deep Cloning)
In deep cloning, we have to copy fields one by one. If we have a field with nested objects such as List, Map, etc. then we have to write the code to copy them too one by one. That’s why it’s called deep cloning or deep copy.
在深度克隆中,我们必须一一复制字段。 如果我们有一个包含嵌套对象(例如List,Map等)的字段,那么我们必须编写代码以将它们也一张一张地复制。 这就是为什么将其称为深度克隆或深度复制。
We can override the Employee clone method like the following code for deep cloning.
我们可以像下面的代码那样覆盖Employee克隆方法,以进行深度克隆。
public Object clone() throws CloneNotSupportedException {
Object obj = super.clone(); //utilize clone Object method
Employee emp = (Employee) obj;
// deep cloning for immutable fields
emp.setProps(null);
Map<String, String> hm = new HashMap<>();
String key;
Iterator<String> it = this.props.keySet().iterator();
// Deep Copy of field by field
while (it.hasNext()) {
key = it.next();
hm.put(key, this.props.get(key));
}
emp.setProps(hm);
return emp;
}
With this clone() method implementation, our test program will produce the following output.
使用此clone()方法实现,我们的测试程序将产生以下输出。
emp and clonedEmp == test: false
emp and clonedEmp HashMap == test: false
clonedEmp props:{city=Bangalore, salary=10000}
clonedEmp name:Pankaj
In most of the cases, this is what we want. The clone() method should return a new object totally detached from the original instance.
在大多数情况下,这就是我们想要的。 clone()方法应返回一个与原始实例完全分离的新对象。
So if you are thinking to use Object clone and cloning in your program, do it wisely and override it properly by taking care of mutable fields.
因此,如果您打算在程序中使用对象克隆和克隆,请明智地进行操作,并通过照顾可变字段来适当地覆盖它。
It could be a daunting task if your class extends other class that in turn extends other class and so on. You will have to go all the way in the Object inheritance hierarchy to take care of the deep copy of all the mutable fields.
如果您的课程扩展了其他课程,又扩展了其他课程,这可能是一项艰巨的任务。 您将必须在对象继承层次结构中进行所有操作,以照顾所有可变字段的深层副本。
使用序列化克隆? (Cloning using Serialization?)
One way to easily perform deep cloning is through serialization. But serialization is an expensive procedure and your class should implement Serializable
interface. All the fields and superclasses must implement Serializable too.
轻松执行深度克隆的一种方法是序列化 。 但是序列化是一个昂贵的过程,您的类应实现Serializable
接口。 所有字段和超类也必须实现Serializable。
使用Apache Commons Util (Using Apache Commons Util)
If you are already using Apache Commons Util classes in your project and your class is serializable, then use the below method.
如果您已在项目中使用Apache Commons Util类,并且该类可序列化,则使用以下方法。
Employee clonedEmp = org.apache.commons.lang3.SerializationUtils.clone(emp);
复制构造函数以进行克隆 (Copy Constructor for Cloning)
We can define a copy constructor to create a copy of the object. Why to depend on the Object clone() method at all?
我们可以定义一个复制构造函数来创建对象的副本。 为什么要完全依赖Object clone()方法?
For example, we can have an Employee copy constructor like the following code.
例如,我们可以有一个类似于以下代码的Employee复制构造函数。
public Employee(Employee emp) {
this.setId(emp.getId());
this.setName(emp.getName());
Map<String, String> hm = new HashMap<>();
String key;
Iterator<String> it = emp.getProps().keySet().iterator();
// Deep Copy of field by field
while (it.hasNext()) {
key = it.next();
hm.put(key, emp.getProps().get(key));
}
this.setProps(hm);
}
Whenever we need a copy of employee object, we can get it using Employee clonedEmp = new Employee(emp);
.
每当我们需要员工对象的副本时,都可以使用Employee clonedEmp = new Employee(emp);
获得它Employee clonedEmp = new Employee(emp);
。
However writing copy constructor can be a tedious job if your class has a lot of variables, especially primitive and immutable.
但是,如果您的类具有很多变量,尤其是原始变量和不可变变量,那么编写拷贝构造函数可能是一项繁琐的工作。
Java对象克隆最佳实践 (Java Object Cloning Best Practices)
- Use default Object clone() method only when your class has primitives and immutable variables or you want shallow copy. In case of inheritance, you will have to check all the classes you are extending till the Object level. 仅当您的类具有基元和不可变变量或需要浅表复制时,才使用默认的Object clone()方法。 在继承的情况下,您将必须检查所有扩展到对象级别的类。
- You can also define copy constructor if your class has mostly mutable properties. 如果您的类主要具有可变属性,则还可以定义复制构造函数。
- Utilize Object clone() method by calling
super.clone()
in overridden clone method, then make necessary changes for deep copying of mutable fields. 通过在重写的克隆方法中调用super.clone()
来利用Object clone()方法,然后进行必要的更改以对可变字段进行深度复制 。 - If your class is serializable, you can use serialization for cloning. However, it will come with a performance hit, so do some benchmarking before using serialization for cloning. 如果您的类可序列化,则可以使用序列化进行克隆。 但是,它将带来性能上的损失,因此在使用序列化进行克隆之前,请进行一些基准测试。
- If you are extending a class and it has defined clone method properly using deep copy, then you can utilize default clone method. For example, we have properly defined clone() method in Employee class as follows.
@Override public Object clone() throws CloneNotSupportedException { Object obj = super.clone(); Employee emp = (Employee) obj; // deep cloning for immutable fields emp.setProps(null); Map<String, String> hm = new HashMap<>(); String key; Iterator<String> it = this.props.keySet().iterator(); // Deep Copy of field by field while (it.hasNext()) { key = it.next(); hm.put(key, this.props.get(key)); } emp.setProps(hm); return emp; }
We can create a child class and utilize the superclass deep cloning as follows.
The
EmployeeWrap
class doesn’t have any mutable properties and it’s utilizing superclass clone() method implementation. If there are mutable fields, then you will have to take care of deep copying of only those fields.Here is a simple program to test if this way of cloning works fine or not.
package com.journaldev.cloning; import java.util.HashMap; import java.util.Map; public class CloningTest { public static void main(String[] args) throws CloneNotSupportedException { EmployeeWrap empWrap = new EmployeeWrap(); empWrap.setId(1); empWrap.setName("Pankaj"); empWrap.setTitle("CEO"); Map<String, String> props = new HashMap<>(); props.put("salary", "10000"); props.put("city", "Bangalore"); empWrap.setProps(props); EmployeeWrap clonedEmpWrap = (EmployeeWrap) empWrap.clone(); empWrap.getProps().put("1", "1"); System.out.println("empWrap mutable property value = "+empWrap.getProps()); System.out.println("clonedEmpWrap mutable property value = "+clonedEmpWrap.getProps()); } }
Output:
So it worked perfectly as we expected.
@Override public Object clone() throws CloneNotSupportedException { Object obj = super.clone(); Employee emp = (Employee) obj; // deep cloning for immutable fields emp.setProps(null); Map<String, String> hm = new HashMap<>(); String key; Iterator<String> it = this.props.keySet().iterator(); // Deep Copy of field by field while (it.hasNext()) { key = it.next(); hm.put(key, this.props.get(key)); } emp.setProps(hm); return emp; }
我们可以创建一个子类,并利用超类进行深度克隆,如下所示。
EmployeeWrap
类没有任何可变属性,并且正在使用超类clone()方法实现。 如果存在可变字段,那么您将只需要深度复制那些字段。这是一个简单的程序,用于测试这种克隆方式是否正常工作。
package com.journaldev.cloning; import java.util.HashMap; import java.util.Map; public class CloningTest { public static void main(String[] args) throws CloneNotSupportedException { EmployeeWrap empWrap = new EmployeeWrap(); empWrap.setId(1); empWrap.setName("Pankaj"); empWrap.setTitle("CEO"); Map<String, String> props = new HashMap<>(); props.put("salary", "10000"); props.put("city", "Bangalore"); empWrap.setProps(props); EmployeeWrap clonedEmpWrap = (EmployeeWrap) empWrap.clone(); empWrap.getProps().put("1", "1"); System.out.println("empWrap mutable property value = "+empWrap.getProps()); System.out.println("clonedEmpWrap mutable property value = "+clonedEmpWrap.getProps()); } }
输出:
因此,它可以按我们预期的那样完美运行。
That’s all about Object cloning in java. I hope you got some idea about Java Object clone() method and how to properly override it without any adverse effect.
这就是Java中的对象克隆。 我希望您对Java Object clone()方法以及如何正确重写它而没有任何不利影响有所了解。
Reference: API Doc for Object clone
参考: 用于对象克隆的API Doc
翻译自: https://www.journaldev.com/60/java-clone-object-cloning-java
java中clone方法