java的深拷贝和浅拷贝

我们知道拷贝就是生成一个新对象和原对象一模一样,但是拷贝也是分方式和程度的,我们来看一下什么是浅拷贝什么是深拷贝

浅拷贝

在Java中,java.lang.Object类的clone()方法用于克隆(浅拷贝,属性的指向是相同的)。
该方法创建一个对象的副本,并通过逐字段分配在其上对其进行调用并返回该对象的引用。
要实现浅拷贝需要实现Cloneable接口,该接口里面没有任何方法,它指向的是java.lang.Object类的clone()

1
protected native Object clone() throws CloneNotSupportedException;

它是一个native方法,由C/C++实现
我们看看例子,理解下它为啥叫浅拷贝
首先有一个字典对象类,它实现了序列化()和克隆的接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Data
class Dictionary implements Serializable {

private String name;

private List<String> words;

@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void shallowCopy() {
Dictionary dictionary1 = new Dictionary();
dictionary1.setName("汉语词典");
dictionary1.setWords(new ArrayList<String>() {{
add("你好");
add("浅拷贝");
}});
Dictionary dictionary2 = (Dictionary) dictionary1.clone();
System.out.println(dictionary1 == dictionary2);
dictionary2.getWords().add("新词语");
System.out.println("dictionary1: " + dictionary1.toString());
System.out.println("dictionary2: " + dictionary2.toString());

dictionary1.setName("新名字");
System.out.println("dictionary1: " + dictionary1.toString());
System.out.println("dictionary2: " + dictionary2.toString());
}

运行结果:

1
2
3
4
5
false
dictionary1: Dictionary(name=汉语词典, words=[你好, 浅拷贝, 新词语])
dictionary2: Dictionary(name=汉语词典, words=[你好, 浅拷贝, 新词语])
dictionary1: Dictionary(name=新名字, words=[你好, 浅拷贝, 新词语])
dictionary2: Dictionary(name=汉语词典, words=[你好, 浅拷贝, 新词语])

从结果上我们知道dictionary1,dictionary2不是指向的同一个对象,确实创建了两个对象,但是当第二个对象属性被修改时,第一个对象也跟着变了。
验证了我们之前说的浅拷贝,两个对象的属性指向的是堆中相同的对象。

深拷贝

深拷贝相对于浅拷贝来说就是属性也是新的对象,我们可以将对象的每一个属性也实现cloneable接口,就可以达到深拷贝的效果。我们也可以使用序列化反序列化来实现深拷贝。
首先将Dictionary实现Serializable接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Data
class Dictionary implements Cloneable, Serializable {

private String name;

private List<String> words;

@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private static void deepCopy() throws IOException, ClassNotFoundException {
Dictionary dictionary1 = new Dictionary();
dictionary1.setName("汉语词典");
dictionary1.setWords(new ArrayList<String>() {{
add("你好");
add("浅拷贝");
}});

Dictionary dictionary2 = null;

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(dictionary1);

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream ois = new ObjectInputStream(byteArrayInputStream);
dictionary2 = (Dictionary) ois.readObject();

// 测试方法没关闭流 实际项目记得关闭流
System.out.println(dictionary1 == dictionary2);
dictionary2.getWords().add("新词语");
System.out.println("dictionary1: " + dictionary1.toString());
System.out.println("dictionary2: " + dictionary2.toString());
}

运行结果

1
2
3
false
dictionary1: Dictionary(name=汉语词典, words=[你好, 深拷贝])
dictionary2: Dictionary(name=汉语词典, words=[你好, 深拷贝, 新词语])

我可以看到不管是对象还是它的属性都是独立的