Clone

Clone是Object类下的一个方法,用于产生一个创建一个新的对象副本。clone主要分为深复制和浅复制,下面我们分别介绍clone如何工作,以及如何实现深复制和浅复制。

克隆对象

clone方法会返回原始对象的一个副本,一个良好的clone准则往往满足以下三个基本原则:
(1)a.clone()!=a 克隆对象与原对象在heap上是两个独立的对象
(2)a.clone().getClass()==a.getClass() 克隆的对象必须与原对象类型一致
(3)a.clone().equals(a) 两个对象要想等
这些并非强制要求,为了达到第三个目的,可能要重写equals方法。

Clone工作过程

Object类提供了clone实现,它被声明为protect native,它的具体实现由本地代码完成,对象的复制通过super.clone()来完成。所以任何对象的复制最终通过一系列的调用到达Object的clone方法,它首先会检查当前类是否实现了Cloneable接口,如果没有实现则抛出CloneNotSupportted异常,该异常是一个受检异常。若该类实现了接口,则调用Object的clone方法创建一个拷贝给调用者。而这一过程通过创建一个新对象,然后将对象的各个域直接复制过来的,对于原始的类型和不可变类型这种方式没有任何问题,但对于可变的类型和引用类型,它们将指向相同的对象,所以在修改时容易相互影响。所以Object的clone是一种浅复制,必须覆盖clone方法来实现深复制。

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package standard;
/** * Created by fuyang on 2017/5/15. */
public class Student implements Cloneable{
private int[] money;
private int age;
public Student(int[] money, int age) {
this.money = money;
this.age = age;
}
public int[] getMoney() {
return money;
}
public int getAge() {
return age;
}
public void setMoney(int money) {
this.money[0] = money;
}
public void setAge(int age) {
this.age = age;
}
@Override protected Object clone() throws CloneNotSupportedException {
return new Student(money.clone(),this.age);
// return super.clone();
}
public static void main(String[] args) {
Student student=new Student(new int[]{100},25);
try {
Student cloneStu=(Student) student.clone();
System.out.println(cloneStu.getMoney()[0]);
student.setMoney(200);
System.out.println(student.getMoney()[0]);
System.out.println(cloneStu.getMoney()[0]);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
}
结果:
100
200
100
改为 super.clone();
则:
100
200
200

Tips:

  • 克隆方法用于创建对象的拷贝,为了使用clone方法,类必须实现java.lang.Cloneable接口重写protected方法clone,如果没有实现Clonebale接口会抛出CloneNotSupportedException
  • 在克隆java对象的时候不会调用构造器
  • java提供一种叫浅拷贝(shallow copy)的默认方式实现clone,创建好对象的副本后然后通过赋值拷贝内容,意味着如果你的类包含可变对象,那么原始对象和克隆都将指向相同的内部对象,这是很危险的,因为发生在可变的字段上任何改变将反应到原始对象和副本对象上。为了避免这种情况,重写clone()方法。
谢谢大佬的打赏!