echo

任生命穿梭 时间的角落

0%

Java 深拷贝和浅拷贝

Java 深拷贝和浅拷贝

  在浅拷贝中,如果原型对象的成员变量是基本类型时,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。

  对应的深拷贝,如果时成员变量为引用对象也复制一份给复制对象。

  

实现

  1、新建一个 Person 和 PersonId 类

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
public class Person implements Cloneable {

private String name;
private int age;
private PersonId personId;

public Person(String name, int age, int id) {
this.name = name;
this.age = age;
this.personId = new PersonId(id);
}

public void setId(int id) {
personId.setId(id);
}

public void setAge(int age) {
this.age = age;
}

public Person clone() throws CloneNotSupportedException {
Person cloned = (Person)super.clone();
return cloned;
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", personId=" + personId +
'}';
}
}
class PersonId{
private int id;

public PersonId(int id) {
this.id = id;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

@Override
public String toString() {
return "PersonId{" +
"id=" + id +
'}';
}
}

  2、使用 CloneDemo 类来测试浅拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class CloneDemo {
public static void main(String[] args) throws CloneNotSupportedException {
Person person = new Person("zhangsan",20,123);
Person cloned = person.clone();

System.out.println("original: "+person);
System.out.println("cloned: "+cloned);

System.out.println("Modify Age and Id: ");
cloned.setAge(55);
cloned.setId(234);

System.out.println("original: "+person);
System.out.println("cloned: "+cloned);
}
}

  3、输出

img

 可以看到我们修改了拷贝对象的年龄和ID,原始对象的年龄还是20,原始对象的ID却变成了我们修改后的值,我们并没有对原始对象的ID进行修改,这里说明浅拷贝对于对象仅仅是拷贝了一个对象的引用而已。

  4、接下来我们修改 Person 类的 clone() 方法,实现深拷贝。

1
2
3
4
5
public Person clone() throws CloneNotSupportedException {
Person cloned = (Person)super.clone();
cloned.personId = personId.clone();
return cloned;
}

  5、为了拷贝PersonId 的对象我们需要PersonId 类实现 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
class PersonId implements Cloneable{
private int id;

public PersonId(int id) {
this.id = id;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

@Override
public String toString() {
return "PersonId{" +
"id=" + id +
'}';
}

public PersonId clone() throws CloneNotSupportedException {
return (PersonId)super.clone();
}
}

  6、再次运行 CloneDemo 输出

img

 可以看到 原始对象的ID 值没有被修改。

  7、使用序列化实现深拷贝

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
import java.io.*;

public class Person implements Cloneable, Serializable {

private String name;
private int age;
private PersonId personId;

public Person(String name, int age, int id) {
this.name = name;
this.age = age;
this.personId = new PersonId(id);
}

public void setId(int id) {
personId.setId(id);
}

public void setAge(int age) {
this.age = age;
}

public Person clone() throws CloneNotSupportedException {

try {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
try(ObjectOutputStream out = new ObjectOutputStream(bout)) {
out.writeObject(this);
}

try(InputStream bin = new ByteArrayInputStream(bout.toByteArray())){
ObjectInputStream in = new ObjectInputStream(bin);
return (Person)in.readObject();
}
} catch (IOException | ClassNotFoundException e) {
CloneNotSupportedException e2 = new CloneNotSupportedException();
e2.initCause(e);
throw e2;
}
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", personId=" + personId +
'}';
}
}

class PersonId implements Cloneable,Serializable{
private int id;

public PersonId(int id) {
this.id = id;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

@Override
public String toString() {
return "PersonId{" +
"id=" + id +
'}';
}

public PersonId clone() throws CloneNotSupportedException {
return (PersonId)super.clone();
}
}

  所有写入流的对象都要实现 Serializable 接口。将 Person 对象写入流中然后再从流中读取出来实现深拷贝。

   输出:

img

总结

  若要实现深拷贝,如果对象中引用了其他对象,必须将引用的对象也克隆。