深拷贝、浅拷贝 和普通的对象赋值的作用和区别

解决问题:深拷贝、浅拷贝 和普通的对象赋值有什么区别?

对象复制

例如:Person p2 = p1;实质就是对象地址复制。把p1地址赋值给p2。此时二者同时指向一块堆内存,所以改变p1的属性值之后,p2所对应的属性值也会跟着变化。

例如有一个如下所示的Person类:

java bean 拷贝 java 拷贝对象_java bean 拷贝

java bean 拷贝 java 拷贝对象_浅拷贝_02

1 package tudou.javabasic.clone;
 2 
 3 class Person {
 4     private int age;
 5     private String name;
 6     private Address address;
 7 
 8     public Address getAddress() {
 9         return address;
10     }
11 
12     public void setAddress(Address address) {
13         this.address = address;
14     }
15 
16     public Person(int age, String name) {
17         this.age = age;
18         this.name = name;
19     }
20 
21     public int getAge() {
22         return age;
23     }
24 
25     public void setAge(int age) {
26         this.age = age;
27     }
28 
29     public String getName() {
30         return name;
31     }
32 
33     public void setName(String name) {
34         this.name = name;
35     }
36 
37     @Override
38     public String toString() {
39         return "Person{" +
40                 "age=" + age +
41                 ", name='" + name + '\'' +
42                 '}';
43     }
44 }

Person

执行如下代码:

java bean 拷贝 java 拷贝对象_java bean 拷贝

java bean 拷贝 java 拷贝对象_浅拷贝_02

1 public class CloneTest {
2     public static void main(String[] args) {
3         Person p1 = new Person(1, "first");
4         Person p2 = p1;//把p1的引用赋值给p2
5         System.out.println("p2.name before:"+p2.getName());
6         p1.setName("second");
7         System.out.println("p2.name after:"+p2.getName());
8     }
9 }

CloneTest

输出结果为:

p2.name before:first
p2.name after:second

如果在改变p1的值之后不想改变p2的值,应该如何处理呢?这时候需要用到拷贝,拷贝用到的函数为object的clone()方法

深拷贝和浅拷贝

clone()方法:

创建一个新对象,然后将当前对象的非静态字段复制到该新对象,

如果字段是值类型的,那么对该字段执行复制;

如果该字段是引用类型的话,则复制引用但不复制引用的对象。(这个称为浅拷贝)

原始对象及其副本引用同一个对象。

这个也就是说:如果使用clone()方法,对于值类型直接复制,对于引用类型 则还是采用复制 引用地址的方式。

代码如下:

java bean 拷贝 java 拷贝对象_java bean 拷贝

java bean 拷贝 java 拷贝对象_浅拷贝_02

1 package tudou.javabasic.clone;
 2 
 3 /**
 4  * Created by tudou on 2017-02-22.
 5  * 浅拷贝
 6  */
 7 public class ShallowCopyPerson implements Cloneable {
 8     private int age;
 9     private String name;
10     private Address address;
11 
12     public ShallowCopyPerson(int age, String name, Address address) {
13         this.age = age;
14         this.name = name;
15         this.address = address;
16     }
17     public Object clone() {
18         try {
19             return (ShallowCopyPerson)super.clone();
20         } catch (Exception e) {
21             e.printStackTrace();
22             return null;
23         }
24     }
25 
26     public Address getAddress() {
27         return address;
28     }
29 
30     public void setAddress(Address address) {
31         this.address = address;
32     }
33 
34     public int getAge() {
35         return age;
36     }
37 
38     public void setAge(int age) {
39         this.age = age;
40     }
41 
42     public String getName() {
43         return name;
44     }
45 
46     public void setName(String name) {
47         this.name = name;
48     }
49 
50 
51     @Override
52     public String toString() {
53         return "ShallowCopyPerson{" +
54                 "age=" + age +
55                 ", name='" + name + '\'' +
56                 ", address=" + address +
57                 '}';
58     }
59 }

ShallowCopyPerson

ShallowCopyPerson 类扩展Cloneable接口,重点关注的方法是clone()方法,这里只是简单使用:

1 public Object clone() {
2         try {
3             return (ShallowCopyPerson)super.clone();
4         } catch (Exception e) {
5             e.printStackTrace();
6             return null;
7         }
8     }

接下来使用ShallowCopyPerson 类,来观察下列代码的运行结果:

java bean 拷贝 java 拷贝对象_java bean 拷贝

java bean 拷贝 java 拷贝对象_浅拷贝_02

1 //对象浅拷贝
 2     private static void shallowCopyTest() {
 3         Address address = new Address("Henan", "zhoukou");
 4         ShallowCopyPerson shallowCopyPerson = new ShallowCopyPerson(
 5                 18,
 6                 "tudou",
 7                 address
 8         );
 9         ShallowCopyPerson personClone = (ShallowCopyPerson) shallowCopyPerson.clone();
10         System.out.println("personClone info before:" + personClone.toString());
11         System.out.println("shallowCopyPerson info before:" + shallowCopyPerson.toString());
12         //这里改变原 shallowCopyPerson的值
13         shallowCopyPerson.setName("new tudou");
14         shallowCopyPerson.setAge(19);
15         //改变address的地址值
16         address.setCity("fj");
17         address.setProvince("fz");
18         shallowCopyPerson.setAddress(address);
19         System.out.println("personClone info after:" + personClone.toString());
20         System.out.println("shallowCopyPerson info before:" + shallowCopyPerson.toString());
21     }

shallowCopyTest

结果如下:

1 ersonClone info before:ShallowCopyPerson{age=18, name='tudou', address=Address{province='Henan', city='zhoukou'}}
2 shallowCopyPerson info before:ShallowCopyPerson{age=18, name='tudou', address=Address{province='Henan', city='zhoukou'}}
3 personClone info after:ShallowCopyPerson{age=18, name='tudou', address=Address{province='fz', city='fj'}}
4 shallowCopyPerson info before:ShallowCopyPerson{age=19, name='new tudou', address=Address{province='fz', city='fj'}}

从结果可以看到:age和name字段 在原对象shallowCopyPerson的属性改变之后 personclone并未改变。但是,address中的字段province和city均有改变!这种方式属于浅拷贝,即clone()方法是浅拷贝。

如何使得address中的字段值也不改变呢?就需要用到深拷贝。

java bean 拷贝 java 拷贝对象_java bean 拷贝

java bean 拷贝 java 拷贝对象_浅拷贝_02

1 package tudou.javabasic.clone;
 2 
 3 /**
 4  * Created by tudou on 2017-02-22.
 5  * 深拷贝
 6  */
 7 public class DeepCopyPerson implements Cloneable {
 8     private int age;
 9     private String name;
10     private Address address;
11 
12     public DeepCopyPerson(int age, String name, Address address) {
13         this.age = age;
14         this.name = name;
15         this.address = address;
16     }
17     public Object clone() {
18         try {
19             return (DeepCopyPerson)super.clone();
20         } catch (Exception e) {
21             e.printStackTrace();
22             return null;
23         }
24     }
25 
26     public Address getAddress() {
27         return address;
28     }
29 
30     public void setAddress(String province, String city) {
31         address = new Address(province,city);
32         address.setCity(city);
33         address.setProvince(province);
34     }
35 
36     public int getAge() {
37         return age;
38     }
39 
40     public void setAge(int age) {
41         this.age = age;
42     }
43 
44     public String getName() {
45         return name;
46     }
47 
48     public void setName(String name) {
49         this.name = name;
50     }
51 
52 
53     @Override
54     public String toString() {
55         return "ShallowCopyPerson{" +
56                 "age=" + age +
57                 ", name='" + name + '\'' +
58                 ", address=" + address +
59                 '}';
60     }
61 }

DeepCopyPerson

运行下面代码:

java bean 拷贝 java 拷贝对象_java bean 拷贝

java bean 拷贝 java 拷贝对象_浅拷贝_02

1     //对象深拷贝
 2     private static void deepCopyTest() {
 3         Address address = new Address("Henan", "zhoukou");
 4         DeepCopyPerson deepCopyPerson = new DeepCopyPerson(
 5                 18,
 6                 "tudou",
 7                 address
 8         );
 9         DeepCopyPerson personClone = (DeepCopyPerson) deepCopyPerson.clone();
10         System.out.println("personClone info before:" + personClone.toString());
11         //这里改变原 shallowCopyPerson的值
12         deepCopyPerson.setName("new tudou");
13         deepCopyPerson.setAge(19);
14         //改变address的地址值
15 //        address.setCity("zhengzhou");
16         deepCopyPerson.setAddress("fj","fz");
17         System.out.println("personClone info after:" + personClone.toString());
18     }

deepCopyTest

结果如下:

personClone info before:ShallowCopyPerson{age=18, name='tudou', address=Address{province='Henan', city='zhoukou'}}
personClone info after:ShallowCopyPerson{age=18, name='tudou', address=Address{province='Henan', city='zhoukou'}}

可以看到这里值完全没有改变。这里实现的深拷贝只是简单的来实现效果,不做效率方面的考虑。

总结

对象赋值:把一个对象的地址复制给另一个对象,二者都指向堆栈中的地址。所以一个对象中的值变了,另一个也会变。

浅拷贝:对于基本类型,克隆对象和原对象相互独立,没有影响,对于引用类型,复制的还是地址值,所以一个改变了,另一个也会改变。

深拷贝:原对象和克隆对象相互独立,不受影响。