Java 深克隆与浅克隆

本文最后更新于 2024年9月25日 下午

Java 深克隆与浅克隆

定义

浅克隆

在克隆对象时,只复制对象本身及其内部引用的基本数据类型字段的值,而不复制引用类型字段所指向的对象.这意味着克隆后的对象和原始对象会共享一部分引用对象

下面是一份Java代码样例.

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
57
58
59
60
class Adventurer implements Cloneable {
public String name;
public Weapon wp;
public Adventurer(){}
public Adventurer(String name, int atk){
this.name = name;
this.wp = new Weapon(atk);
}

@Override
public Adventurer clone() throws CloneNotSupportedException{
return (Adventurer) super.clone();
}

public void setName(String name){
this.name = name;
}

public String getName(){
return name;
}
}

class Weapon{
private int atk;
public Weapon(){}
public Weapon(int atk){
this.atk = atk;
}
public Weapon(Weapon wp){
this.atk = wp.atk;
}
public void setAtk(int atk){
this.atk = atk;
}
public int getAtk(){
return atk;
}
}

public class TestClone {
public static void main(String[] args) {
Adventurer adv1 = new Adventurer("xiaohong",8);
try {
Adventurer adv2 = adv1.clone();
adv2.wp.setAtk(10);
adv2.setName("xiaoming");
System.out.println(adv1.wp.getAtk() + " " + adv2.wp.getAtk());
System.out.println(adv1.getName() + " " + adv2.getName());
}catch(Exception e){
System.out.println(e);
}
}

}
/*output:
10 10
xiaohong xiaoming
*/

可以看到,代码对Adventurer类进行了clone()方法的重载,但并没有对其引用类Weapon类进行克隆.这样的克隆方法只复制了Adventurer类所持有的name,而没有复制Weapon.

深克隆

我们对重载clone()方法做修改,使其可以复制引用类Weapon类

1
2
3
4
5
6
@Override
public Adventurer clone() throws CloneNotSupportedException{
Adventurer copy = (Adventurer) super.clone();
copy.wp = new Weapon(this.wp);
return copy;
}

这里我们调用Weapon的创建方法,使其复制了一个新的Weapon并将其作为复制对象的引用类.代码重新运行的结果如下:

1
2
8 10
xiaohong xiaoming

可以看到,对adv2(第二个Adventurer类)的Weapon进行修改,并不会改变adv1的Weapon的atk值,说明两个Adventurer类所引用的Weapon类已不是同一个.

深克隆方法

在 Java 中,常见的实现深克隆的方式有:

  1. 通过继承 Cloneable 接口,重写 clone() 方法实现深克隆;
  2. 通过序列化与反序列化的方式实现深克隆;
  3. 第三方工具类实现深克隆,克隆对象需继承 Serializable 接口。

Override clone()

前文已经介绍这种方法.

Object类中的clone()方法的声明中包含了throws CloneNotSupportedException,因此如果你的类实现了Cloneable接口并重写了clone()方法,而在该方法中调用了super.clone(),那么你的方法也需要声明会抛出CloneNotSupportedException异常。

1
2
3
4
5
6
@Override
public Adventurer clone() throws CloneNotSupportedException{
Adventurer copy = (Adventurer) super.clone();// 复制本类
copy.wp = new Weapon(this.wp);// 复制引用类
return copy;//返回复制对象
}

Serialize and Deserialize

要实现序列化,我们首先改变Adventurer类与Weapon类的接口,将其修改为Serializable

1
2
class Adventurer implements Serializable
class Weapon implements Serializable

通过新建DeepCopyCloneUtil类搭建deepClone(T object)方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class DeepCopyCloneUtil {
public static <T> T deepClone(T object) throws IOException, ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(object);
oos.close();

ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
T clonedObject = (T) ois.readObject();
ois.close();

return clonedObject;
}
}

main()中运行以下语句:

1
2
3
4
5
6
7
8
9
10
Adventurer adv3 = new Adventurer("liming", 23);
try {
Adventurer adv4 = DeepCopyCloneUtil.deepClone(adv3);
adv4.setName("wangming");
adv4.wp.setAtk(999);
System.out.println(adv3.getName() + " " + adv4.getName());
System.out.println(adv3.wp.getAtk() + " " + adv4.wp.getAtk());
} catch (Exception e) {
System.out.println(e);
}

得到结果为:

1
2
liming wangming
23 999

Third-Party Tools

打开IDEA,文件->项目结构->库,点击"+",选择"来自Maven",输入com.google.code.gson进行搜索,下载对应安装包,对应导入语句为:

1
import com.google.gson.Gson;

我们利用Gson这个第三方库实现深克隆

1
2
3
4
5
6
7
8
Adventurer adv5 = new Adventurer("Bob", 19);
Gson gson = new Gson();
Adventurer adv6 = gson.fromJson(gson.toJson(adv5), Adventurer.class);

adv6.setName("Alice");
adv6.wp.setAtk(100);
System.out.println(adv5.getName() + " " + adv6.getName());
System.out.println(adv5.wp.getAtk() + " " + adv6.wp.getAtk());

运行结果:

1
2
Bob Alice
19 100

附:整体代码

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import java.io.*;
import com.google.gson.Gson;

class Adventurer implements Serializable, Cloneable{
public String name;
public Weapon wp;

public Adventurer() {
}

public Adventurer(String name, int atk) {
this.name = name;
this.wp = new Weapon(atk);
}

@Override
public Adventurer clone() throws CloneNotSupportedException {
Adventurer copy = (Adventurer) super.clone();
copy.wp = new Weapon(this.wp);
return copy;
}

public void setName(String name) {
this.name = name;
}

public String getName() {
return name;
}
}

class Weapon implements Serializable {
private int atk;

public Weapon() {
}

public Weapon(int atk) {
this.atk = atk;
}

public void setAtk(int atk) {
this.atk = atk;
}

public int getAtk() {
return atk;
}

public Weapon(Weapon wp) {
this.atk = wp.atk;
}
}

class DeepCopyCloneUtil {
public static <T> T deepClone(T object) throws IOException, ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(object);
oos.close();

ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
T clonedObject = (T) ois.readObject();
ois.close();

return clonedObject;
}
}

public class TestClone {
public static void main(String[] args) {
Adventurer adv1 = new Adventurer("xiaohong", 8);
try {
Adventurer adv2 = adv1.clone();
adv2.wp.setAtk(10);
adv2.setName("xiaoming");
System.out.println(adv1.wp.getAtk() + " " + adv2.wp.getAtk());
System.out.println(adv1.getName() + " " + adv2.getName());
} catch (Exception e) {
System.out.println(e);
}

Adventurer adv3 = new Adventurer("liming", 23);
try {
Adventurer adv4 = DeepCopyCloneUtil.deepClone(adv3);
adv4.setName("wangming");
adv4.wp.setAtk(999);
System.out.println(adv3.getName() + " " + adv4.getName());
System.out.println(adv3.wp.getAtk() + " " + adv4.wp.getAtk());
} catch (Exception e) {
System.out.println(e);
}

Adventurer adv5 = new Adventurer("Bob", 19);
Gson gson = new Gson();
Adventurer adv6 = gson.fromJson(gson.toJson(adv5), Adventurer.class);

adv6.setName("Alice");
adv6.wp.setAtk(100);
System.out.println(adv5.getName() + " " + adv6.getName());
System.out.println(adv5.wp.getAtk() + " " + adv6.wp.getAtk());
}

}

/*
8 10
xiaohong xiaoming
liming wangming
23 999
Bob Alice
19 100
*/

Java 深克隆与浅克隆
https://meteor041.git.io/2024/09/25/Java-深克隆与浅克隆/
作者
meteor041
发布于
2024年9月25日
许可协议