在编程中,拷贝(或复制)是常见的操作之一。拷贝操作用于创建一个新对象或数据结构,使其具有与原对象或数据结构相同或部分相同的值。
在进行拷贝操作时,常见的方式有浅拷贝和深拷贝。本文将重点讨论浅拷贝和深拷贝的概念、实现方式以及它们之间的区别。
浅拷贝是指创建一个新对象,新对象的字段或属性与原对象相同。但对于原对象中的引用类型字段,浅拷贝只会复制其引用而不是实际的数据。
当使用浅拷贝时,新对象和原始对象共享相同的数据引用,意味着它们指向内存中相同的对象。
在内存中,每个对象都被分配了一块内存空间。对象的字段存储在该内存空间中,并在字段中保存对其他对象的引用。当进行浅拷贝时,只复制了对象的字段值,而没有创建新的内存空间。
假设有一个类 Person 如下:
class Person {
private String name;
private Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
// Getter and setter methods...
}
其中包含一个 Address 类型的字段 address。现在假设我们有一个原始对象 person1,其中的 address 字段引用了同一个 Address 对象:
Person person1 = new Person("Alice", new Address("City", "Street"));
当进行浅拷贝时,通过调用 person1.clone() 方法可以得到一个新对象 person2:
Person person2 = person1.clone();
此时,person1 和 person2 是两个独立的对象,但它们的 name 字段和 address 字段引用相同的内存地址。
address 字段在 person1 和 person2 中都指向同一个内存地址,即一个共享的 Address 对象。这意味着,当修改了这个共享的 Address 对象时,无论是通过 person1 还是 person2,修改都会反映在两个对象中。
所以浅拷贝的效果就是对象之间共享相同的引用,对其中一个对象的修改会影响到其他对象。而如果需要实现完全独立的副本,可以使用深拷贝来创建对象的拷贝,并在其中复制引用类型的字段,确保每个副本都有自己独立的对象引用。
Java中,可以通过以下方式实现浅拷贝:
实现Cloneable接口,并重写clone()方法。
深拷贝是指创建一个新对象,新对象的所有字段或属性都是原对象的副本。无论是基本类型还是引用类型,都会被复制到新对象中。
还是上面的例子,下面是深拷贝的实现:
class Person implements Cloneable {
private String name;
private Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public Person clone() throws CloneNotSupportedException {
Person clonedPerson = (Person) super.clone();
// 创建一个新的 Address 对象并复制原始对象的 address 字段值
clonedPerson.address = new Address(this.address.getCity(), this.address.getStreet());
return clonedPerson;
}
// Getter and setter methods...
}
class Address {
private String city;
private String street;
public Address(String city, String street) {
this.city = city;
this.street = street;
}
// Getter and setter methods...
}
在这个示例中,我们通过覆盖 clone() 方法并在其中手动创建一个新的 Address 对象来实现深拷贝。这样就可以确保每个副本都有自己独立的 Address 对象。
可以看到,person1 和 person2 是两个独立的对象,并且它们的 address 字段引用了不同的内存地址,即各自拥有独立的 Address 对象。因此,对其中一个对象的修改不会影响到另一个对象。
需要注意的是,在实现深拷贝时,不仅要复制引用类型的字段,还要确保引用类型的字段也实现了可复制(深拷贝)的逻辑,以防止仍然存在共享的引用。
二维表展示对比
深拷贝 (Deep Copy) | 浅拷贝 (Shallow Copy) | |
---|---|---|
定义 | 创建一个新的对象,将原始对象的所有属性进行递归复制到新对象中 | 创建一个新的对象,将原始对象的属性的引用复制到新对象中 |
独立性 | 深拷贝后的对象和原始对象是完全独立的,对其中一个对象的修改不会影响另一个对象 | 浅拷贝后的对象和原始对象共享同一个属性对象,对其中一个对象的修改会影响另一个对象 |
对象关系 | 深拷贝后的对象和原始对象没有任何关联 | 浅拷贝后的对象和原始对象共享同一个属性对象 |
修改影响 | 对原始对象的修改不会影响深拷贝后的对象 | 对原始对象的修改会影响浅拷贝后的对象 |
对象复制 | 深拷贝会递归复制所有属性对象,创建一个完全独立的对象 | 浅拷贝只复制属性对象的引用,创建一个共享属性对象的新对象 |
通过上述二维表的总结,可以清晰地看到深拷贝和浅拷贝的区别。
深拷贝创建一个完全独立的对象,对原始对象的修改不会影响深拷贝后的对象;而浅拷贝创建一个共享属性对象的新对象,对原始对象的修改会影响浅拷贝后的对象。
对于基本数据类型来说,两者一样,都会复制一份新的。
如果还不太理解,举个例子看会不会好理解些:
原型模式是一种创建型设计模式,它使用原型实例指定要创建的对象类型,并通过复制这个原型来创建新对象。在原型模式中,拷贝可以是浅拷贝或深拷贝,具体取决于如何实现原型对象的复制。
在Java中,Cloneable接口就是原型模式的一种实现方式,它使用浅拷贝来复制对象。如果需要实现深拷贝,则需要在clone()方法中手动处理引用类型的字段或属性的拷贝。
如果想有进一步的了解,可以参看我的上一篇博客
链接: 设计模式之原型模式–超越实例化的魔法,从复制到创造的无限可能