Java 对象克隆()方法 - Java 中的克隆

克隆是创建一个 Object 副本的过程。Java Object 类配备了原生 clone() 方法,该方法返回了现有实例的副本。

Java 对象克隆

java clone object 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. 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.

 1package com.journaldev.cloning;
 2
 3import java.util.HashMap;
 4import java.util.Iterator;
 5import java.util.Map;
 6
 7public class Employee implements Cloneable {
 8
 9    private int id;
10
11    private String name;
12
13    private Map<String, String> props;
14
15    public int getId() {
16    	return id;
17    }
18
19    public void setId(int id) {
20    	this.id = id;
21    }
22
23    public String getName() {
24    	return name;
25    }
26
27    public void setName(String name) {
28    	this.name = name;
29    }
30
31    public Map<String, String> getProps() {
32    	return props;
33    }
34
35    public void setProps(Map<String, String> p) {
36    	this.props = p;
37    }
38
39     @Override
40     public Object clone() throws CloneNotSupportedException {
41     return super.clone();
42     }
43
44}

我们正在使用 Object clone() 方法,所以我们已经实现了 Cloneable 接口(/community/tutorials/interface-in-java)。

使用 Object clone() 方法

让我们创建一个测试程序,使用object clone()方法创建实例副本。

 1package com.journaldev.cloning;
 2
 3import java.util.HashMap;
 4import java.util.Map;
 5
 6public class CloningTest {
 7
 8    public static void main(String[] args) throws CloneNotSupportedException {
 9
10    	Employee emp = new Employee();
11
12    	emp.setId(1);
13    	emp.setName("Pankaj");
14    	Map<String, String> props = new HashMap<>();
15    	props.put("salary", "10000");
16    	props.put("city", "Bangalore");
17    	emp.setProps(props);
18
19    	Employee clonedEmp = (Employee) emp.clone();
20
21    	// Check whether the emp and clonedEmp attributes are same or different
22    	System.out.println("emp and clonedEmp == test: " + (emp == clonedEmp));
23    	
24    	System.out.println("emp and clonedEmp HashMap == test: " + (emp.getProps() == clonedEmp.getProps()));
25    	
26    	// Let's see the effect of using default cloning
27    	
28    	// change emp props
29    	emp.getProps().put("title", "CEO");
30    	emp.getProps().put("city", "New York");
31    	System.out.println("clonedEmp props:" + clonedEmp.getProps());
32
33    	// change emp name
34    	emp.setName("new");
35    	System.out.println("clonedEmp name:" + clonedEmp.getName());
36
37    }
38
39}

输出:

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

克林顿 支持 在 Runtime

如果我们的员工类别不会实施Cloneable界面,上面的程序将投放CloneNotSupportedException运行时间(例外)( / 社区 / 教程 / 例外 - 处理 - 在Java)。

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

Java CloneNotSupportedException

对象克隆的理解

让我们看看上面的输出,并了解对象 clone() 方法发生了什么。

  1. 联合国 `emp和clonedEmp == 测试:假': 这意味着emp和clonedEmp是两个不同的对象,而不是指同一个对象. 这符合Java物体克隆的要求。 ( )2. ‘emp and ClopedEmp HashMap ==: true': 因此emp和clonedEmp对象变量都指同个对象. 如果我们改变基本对象值,这可能是一个严重的数据完整性问题. 值的任何变化也可能被反射到被克隆的实例中.
  2. 被封装 弹出道具 :{city=新} York,工资=1000,职称=CEO: 我们并没有改变克隆Emp的特性,但是,由于emp和克隆Emp两个变量都指同一种对象,所以还是改变了它们. 之所以发生这种情况,是因为默认对象克隆()方法会产生一个浅层复制. 当你想通过克隆过程 创造完全分离的物体时, 这个问题可能会成为问题。 这会导致不想要的结果,因此需要适当覆盖对象克隆方法.
  3. 被封装 英文名:Pankaj ':这里发生了什么? 我们改变了emp名称,但克隆Emp名称没有改变. 这是因为string is immutable. 因此,当我们设置 emp 名称时, 将创建一个新字符串, 并在 `thi. name = name; '. hence cloned 中更改 emp 名称引用 Emp名称保持不变. 任何原始的可变类型也会发现类似的行为. 因此,只要我们只拥有原始和不可改变的变量,我们就能很好地使用 Java 对象默认克隆。 (单位:千美元) (英语)

对象克隆类型

有两种类型的对象克隆 - 浅层克隆和深层克隆. 让我们了解它们中的每一个,并找出在我们的Java程序中实施克隆的最佳方法。

1、克隆

Java Object clone() 方法的默认实现是使用浅副本,它使用 reflection API 来创建实例的副本。

1@Override
2 public Object clone() throws CloneNotSupportedException {
3
4     Employee e = new Employee();
5     e.setId(this.id);
6     e.setName(this.name);
7     e.setProps(this.props);
8     return e;
9}

2、深度克隆

在深度克隆中,我们必须一个接一个地复制字段,如果我们有一个字段包含嵌入的对象,如列表,地图等,那么我们必须写代码来复制它们也一个接一个,这就是为什么它被称为深度克隆或深度复制。

 1public Object clone() throws CloneNotSupportedException {
 2
 3    Object obj = super.clone(); //utilize clone Object method
 4
 5    Employee emp = (Employee) obj;
 6
 7    // deep cloning for immutable fields
 8    emp.setProps(null);
 9    Map<String, String> hm = new HashMap<>();
10    String key;
11    Iterator<String> it = this.props.keySet().iterator();
12    // Deep Copy of field by field
13    while (it.hasNext()) {
14    	key = it.next();
15    	hm.put(key, this.props.get(key));
16    }
17    emp.setProps(hm);
18    
19    return emp;
20}

有了这个 clone() 方法的实现,我们的测试程序将产生以下输出。

1emp and clonedEmp == test: false
2emp and clonedEmp HashMap == test: false
3clonedEmp props:{city=Bangalore, salary=10000}
4clonedEmp name:Pankaj

在大多数情况下,这就是我们想要的。克隆()方法应该返回一个完全与原始实例分开的新对象。所以,如果你在你的程序中考虑使用对象克隆和克隆,请明智地做到这一点,并通过照顾可变字段来妥善排列它。如果你的类扩展到另一个类,这反过来扩展到另一个类等,那么这可能是一个令人恐惧的任务。你必须在对象继承等级层面上走到整个方向来照顾所有可变字段的深度副本。

使用序列化克隆?

简单地进行深度克隆的一种方法是通过 serialization。但 serialization 是一个昂贵的程序,你的班级应该实施Serializable接口。

使用Apache Commons Util

如果您已经在您的项目中使用了 Apache Commons Util 类,并且您的类可以进行序列化,请使用下面的方法。

1Employee clonedEmp = org.apache.commons.lang3.SerializationUtils.clone(emp);

复制制造商为克隆

我们可以定义一个 copy constructor来创建一个对象的副本。

 1public Employee(Employee emp) {
 2    
 3    this.setId(emp.getId());
 4    this.setName(emp.getName());
 5    
 6    Map<String, String> hm = new HashMap<>();
 7    String key;
 8    Iterator<String> it = emp.getProps().keySet().iterator();
 9    // Deep Copy of field by field
10    while (it.hasNext()) {
11    	key = it.next();
12    	hm.put(key, emp.getProps().get(key));
13    }
14    this.setProps(hm);
15
16}

每当我们需要员工对象的副本时,我们可以使用Employee clonedEmp = new Employee(emp);来获取它。

Java 对象克隆最佳实践

  1. 联合国 只有当您的类有原始变量和不可变变量或想要浅复制时,才使用默认对象克隆()方法 。 在继承时,您必须检查您要扩展至对象级别的所有类别 。 2 如果您的类大多具有可变属性,也可以定义复制构建器.
  2. 使用对象克隆方法,在被覆没的克隆方法中称为"super.clone ()",然后对可变字段的深复制作出必要的修改.
  3. 如果你的课是可序列的,你可以使用序列化来进行克隆. 然而,它将伴随着性能打击,在使用克隆序列化之前也有一些基准。 5 如果您正在扩展一个类,并且它已经用深复制正确定义了克隆方法,那么您可以使用默认的克隆方法. 例如,在雇员类别中,我们有如下正确定义的克隆()方法。 (_) ) QQ @ Override 公共对象克隆 () 抛出 克隆不支持 例外 {

对象 obj = super.clone (); (- ) 雇员 emp = (雇员)obj; (- ) (- ) // 为不可移动字段进行深克隆 (- ) emp.set props(null); (- ) 地图 <String, String > (- ) 字符串键; (- ) 调度器 =此.props.keySet(. 深处 按字段复制字段 ,而(it.hasNext ()){ 密钥= it.next (); hm.put(key, this.props.get(key)); } emp.setProps(hm); (_)

返回 emp; } __ 我们可以通过以下方式创建儿童类并使用超类的深层克隆:\ 包 com.journaldev.cloning; (- )(- )(- )(- )(- )(- )(-)包 com.journal.cloning;(- )(- )(- )(-)(- )(-)公共无效设置 Title(Title(tring-t)(- )此标题=t; (- )(- )(- )(- )(-KBR1_)(-)公共对象克隆抛出(-) 克隆不支持 例外 {

返回 super.clone (); } } __ `雇员Wrap'类没有任何可变特性,它正在利用超级类克隆()方法的实施。 如果有可变的字段,那么你必须只注意这些字段的深层复制. 这里有一个简单的程序来测试这种克隆方式是否有效. {} package com.journaldev.cloning;

导入java. util. HashMap; 导入java. util. 映射;

公共类 Cloning Test {

公共静态空心主(String [] args) 抛出 克隆不支持例外 {

雇员Wrap empWrap = 新雇员Wrap ();

empWrap.set.(1); empWrap.setName ("Pankaj"); empWrap.set Title ("CEO"); 地图 <String, String > popps =新HashMap<> () (); pops. put("sary","1000"); pops.put("城市","Bangalore"); empWrap.setProps(Props(ps); (");

员工被克隆 empWrap = (雇员Wrap) empWrap.clone ();

empWrap.getProps (. put ("1","1"); (_)

System.out.println ("empWrap可变属性值="+empWrap.getProps ()"); ( _ System.out.println ("已克隆") empWrap可变属性值="+克隆EmpWrap.getProps ()"; (- ) (- ) (- ) (- ) (- ) (-出) (-- ) emplap可变属性值={1=1=1, 城市=班加洛尔, 工资=1000) (- ) 克隆. EmpWrap可变属性值={城市=Bangalore,工资=1000} . 所以,它完美地工作 正如我们预期。 (单位:千美元) (英语)

我希望你对Java Object clone()方法有了一些想法,以及如何正确地在没有任何不利影响的情况下对其进行克隆。

您可以从我的GitHub存储库(https://github.com/journaldev/journaldev/tree/master/CoreJavaProjects/Java-Cloning)下载该项目。

参考: API Doc for Object clone

Published At
Categories with 技术
Tagged with
comments powered by Disqus