简介

  • 浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递的拷贝。
  • 深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象并复制其内容,做到了真正完全的拷贝。

浅拷贝实现

以下场景中有两个类,一个是Parent,一个是Child

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Parent implements Cloneable {
String parentName;
Child child;

public Parent(String parentName, Child child) {
this.parentName = parentName;
this.child = child;
}

@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

public class Child {
String childName;

public Child(String childName) {
this.childName = childName;
}
}

要实现浅拷贝,只需要让Parent实现Cloneable接口,并覆盖Objectclone()方法即可。以下为测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Parent parent1 = new Parent("parent name", new Child("child name"));
Parent parent2 = (Parent) parent1.clone();
System.out.println(parent1 == parent2);
System.out.println(parent1.parentName);
System.out.println(parent2.parentName);
System.out.println("==================");
System.out.println(parent1.child == parent2.child);
System.out.println(parent1.child.childName);
System.out.println(parent2.child.childName);
}
}

控制台输出:

1
2
3
4
5
6
7
false
parent name
parent name
==================
true
child name
child name

可以看出,浅拷贝确实创建了一个新的Parent对象,并且属性parentName的值也一模一样,但是对于为引用类型的属性child,实际上与之前引用的是同一个对象。

深拷贝实现

要实现深拷贝,常用的方案有以下两种:

  1. 序列化这个对象,再反序列化回来,就可以得到新的对象。
  2. 让属性也实现Cloneable

属性实现Cloneable

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
public class Parent implements Cloneable {
String parentName;
Child child;

public Parent(String parentName, Child child) {
this.parentName = parentName;
this.child = child;
}

@Override
protected Object clone() throws CloneNotSupportedException {
Parent parentClone = (Parent) super.clone();
parentClone.child = (Child) this.child.clone();
return parentClone;
}
}

public class Child implements Cloneable {
String childName;

public Child(String childName) {
this.childName = childName;
}

@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

和之前的浅拷贝不同之处在于这里Child也实现了Cloneable,并覆盖了clone()方法,而Parentclone()方法也有略微不同,在调用了clone()方法后还调用了属性childclone()方法重新设置属性,从而实现完完全全的拷贝。

此时控制台输出如下:

1
2
3
4
5
6
7
false
parent name
parent name
==================
false
child name
child name

注意到此时原始对象和拷贝对象的child属性所引用的不再是同一个对象了。

序列化方式

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
public class Parent implements Serializable {
String parentName;
Child child;

public Parent(String parentName, Child child) {
this.parentName = parentName;
this.child = child;
}

public Object deepClone() throws Exception {
// 将对象写到流里
OutputStream bo = new ByteArrayOutputStream();
//OutputStream op = new ObjectOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(this);

// 从流里读对象出来
InputStream bi = new ByteArrayInputStream(((ByteArrayOutputStream) bo).toByteArray());
ObjectInputStream oi = new ObjectInputStream(bi);
return (oi.readObject());
}
}

public class Child implements Serializable {
String childName;

public Child(String childName) {
this.childName = childName;
}
}

此时两个类都不需要再实现Cloneable接口并覆盖clone()方法了,但是它们都需要实现Serializable接口,并且在Parent的克隆方法中要实现序列化反序列化的逻辑,此时控制台输出如下:

1
2
3
4
5
6
7
false
parent name
parent name
==================
false
child name
child name