本文来自于我提供的免费的 Java 8 课程,里面会针对深拷贝和浅拷贝的不同之处进行讨论。你可以在这儿下载到PPT以及PDF文档。 什么是拷贝?开始之前,我要先强调一下 Java 中的拷贝是什么。首先,让我们对引用拷贝和对象拷贝进行一下区分。 引用拷贝, 正如它的名称所表述的意思, 就是创建一个指向对象的引用变量的拷贝。如果我们有一个 Car 对象,而且让 myCar 变量指向这个变量,这时候当我们做引用拷贝,那么现在就会有两个 myCar 变量,但是对象仍然只存在一个。
示例 1 对象拷贝会创建对象本身的一个副本。因此如果我们再一次服务我们 car 对象,就会创建这个对象本身的一个副本, 同时还会有第二个引用变量指向这个被复制出来的对象。
示例 2 |
什么是对象?
|
浅拷贝
|
1
2
3
4
5
6
7
8
9
10
|
public class Person { private Name name; private Address address; public Person(Person originalPerson) { this .name = originalPerson.name; this .address = originalPerson.address; } […] } |
示例 5
浅拷贝的问题就是两个对象并非独立的。如果你修改了其中一个 Person 对象的 Name 对象,那么这次修改也会影响奥另外一个 Person 对象。
让我们在示例中看看这个问题。假如说我们有一个 Person 对象,然后也会有一个引用变量 monther 来指向它;然后当我们对 mother 进行拷贝时,创建第二个 Person 对象 son。如果在此后的代码中, son 尝试用 moveOut() 来修改他的 Address 对象, 那么 mother 也会跟着他一起搬走!
1
2
3
4
5
|
Person mother = new Person( new Name(…), new Address(…)); […] Person son = new Person(mother); […] son.moveOut( new Street(…), new City(…)); |
示例 6
这种现象之所以会发生,是因为 mother 和son 对象共享了相同的 Address 对象,如你在示例 7 中所看到的描述。当我们在一个对象中修改了 Address 对象,那么也就表示两个对象总的 Address 都被修改了。
示例 7
深拷贝
|
1
2
3
4
5
6
7
8
9
10
|
public class Person { private Name name; private Address address; public Person(Person otherPerson) { this .name = new Name(otherPerson.name); this .address = new Address(otherPerson.address); } […] } |
示例 9
使用这种深拷贝,我们可以重新尝试示例 6 中的 mother-son 这个用例。现在 son 可以成功的搬走了!
不过,故事到这儿并没有结束。要创建一个真正的深拷贝,就需要我们一直这样拷贝下去,一直覆盖到 Person 对象所有的内部元素, 最后只剩下原始的类型以及“不可变对象(Immutables)”。让我们观察下如下这个 Street 类以获得更好的理解:
1
2
3
4
5
6
7
8
9
10
|
public class Street { private String name; private int number; public Street(Street otherStreet){ this .name = otherStreet.name; this .number = otherStreet.number; } […] } |
示例 10
Street 对象有两个实体变量组成 – String 类型的 name 以及 int 类型的 number。int 类型的 number 是一个原始类型,并非对象。它只是一个简单的值,不能共享, 因此在创建第二个实体变量时,我们可以自动创建一个独立的拷贝。String 是一个不可变对象(Immutable)。简言之,不可变对象也是对象,可一旦创建好了以后就再也不能被修改了。因此,你可以不用为其创建深拷贝就能对其进行共享。
总结
|
1
2
3
4
5
|
Person mother = new Person( new Name(…), new Address(…)); […] Person son = new Person(mother); […] son.moveOut( new Address(...)); |
示例 11
在面向对象领域,这样做违背了封装的原则,因此应该被避免。封装是面向对象编程中一个最重要的方面。在这里,我已经违背封装的原则,对 Person 类中 Address 对象的内部细节进行了访问。这样做对我们的代码造成了伤害,因为我们现在跟 Person 类中的 Address 类纠缠在一起,如果对 Address 类进行了修改,就会如我上面所解释的对代码造成伤害。不过是你显然是会需要将你定义的各种类互相联系在一起以构成代码工程的,但在你要将两个类联系在一起时,需要好好分析一下成本和收益。
本文标题:Java 中的浅拷贝与深拷贝
本文地址:https://www.oschina.net/translate/java-copy-shallow-vs-deep-in-which-you-will-swim
参与翻译:leoxu
英文原文:Shallow vs. Deep Copy in Java