Shaoqun Liu's blog
搜索文档…
浅拷贝与深拷贝

0x00 浅拷贝和深拷贝

浅拷贝会创建一个新对象,然后将当前对象的非静态字段复制到该新对象,如果字段是值类型的,那么对该字段执行复制。如果该字段是引用类型的话,则复制引用但不复制引用的对象。因此,原始对象及其副本引用同一个对象。看如下代码:
1
public class Main {
2
3
static class Test {
4
private ArrayList<String> strings = new ArrayList<>();
5
6
public void addString(String s) {
7
strings.add(s);
8
}
9
10
@Override
11
public String toString() {
12
StringBuilder stringBuilder = new StringBuilder();
13
for (String s : strings) {
14
stringBuilder.append(s).append(" ");
15
}
16
return stringBuilder.toString();
17
}
18
}
19
20
public static void main(String[] args) {
21
Test t1 = new Test();
22
t1.addString("Hello");
23
System.out.println(t1); // 输出t1
24
Test t2 = t1; // 浅拷贝
25
t2.addString("World!");
26
System.out.println(t1); // 输出t1
27
}
28
}
Copied!
此代码的输出值为:
1
Hello
2
Hello World!
Copied!
因为代码Test t2 = t1;为浅拷贝,对象t2中的strings为引用类型变量,在拷贝的过程中,仅仅是把t1中的那个strings的地址赋值给t2中的引用类型变量strings,其仍等于对象t1中的那个strings所指向的内存地址值。所以当我们使用t2.addString("World!");时,仍然相当于给t1中的strings添加元素。所以第二行的输出为Hello World!
深拷贝则恰恰相反,深拷贝会创建一个新对象,然后将当前对象的非静态字段复制到该新对象,无论该字段是值类型的还是引用类型,都复制独立的一份当你修改其中一个对象的任何内容时,都不会影响另一个对象的内容

0x01 深拷贝的实现

深拷贝的实现需要类实现Cloneable接口,而且需要重写其clone方法,以手动实现深拷贝。当拷贝的时候,需要调用clone方法进行深拷贝,如果仍然使用=赋值,那么结果仍为浅拷贝。
1
public class Main {
2
3
static class Test implements Cloneable {
4
private ArrayList<String> strings = new ArrayList<>();
5
6
public void addString(String s) {
7
strings.add(s);
8
}
9
10
@Override
11
protected Object clone() throws CloneNotSupportedException {
12
Test test = (Test) super.clone();
13
// 重新创建一个新的ArrayList
14
// 然后手工复制其中的值到新的对象中以实现深拷贝
15
test.strings = new ArrayList<>();
16
for (String s : strings) {
17
test.addString(s);
18
}
19
return test;
20
}
21
22
@Override
23
public String toString() {
24
StringBuilder stringBuilder = new StringBuilder();
25
for (String s : strings) {
26
stringBuilder.append(s).append(" ");
27
}
28
return stringBuilder.toString();
29
}
30
}
31
32
public static void main(String[] args) throws CloneNotSupportedException {
33
Test t1 = new Test();
34
t1.addString("Hello");
35
System.out.println(t1);
36
Test t2 = (Test) t1.clone(); // 深拷贝
37
t2.addString("World!");
38
System.out.println(t1);
39
}
40
}
Copied!
此代码输出即为:
1
Hello
2
Hello
Copied!
因为t2t1中的strings分别为指向两块不同内存区域的引用类型变量。当然我们也可以通过对象的序列化来实现深拷贝。