引用拷贝,浅拷贝,深拷贝
参考资料
水平有限,欢迎交流!
kimi
【【每天一个技术点】引用拷贝、浅拷贝、深拷贝】
一文搞懂Java引用拷贝、浅拷贝、深拷贝 - bigsai - 博客园 (cnblogs.com)
【黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难】
1. 引用拷贝 (起绰号)
引用拷贝并不是真正意义上的拷贝,而是创建了一个新的指针指向已存在的数据。在这种拷贝方式下,原始数据和拷贝数据在内存中占用相同的位置。
特点:
- 不创建新的对象,只是创建了一个新的引用(指针)指向已存在的对象。
- 修改其中一个对象会影响另一个对象,因为他们指向同一个内存地址。
2. 浅拷贝(只拷贝基本数据类型,引用类型未在堆区开辟内存)
浅拷贝会创建一个新的对象,其字段值与原始对象相同。但是,如果原始对象的字段是引用类型,则拷贝的是引用,而不是实际的对象。
特点:
- 创建一个新的对象,并将原始对象的值复制到新对象中。
- 如果字段是基本类型,拷贝的是值。
- 如果字段是引用类型,拷贝的是引用,而不是引用的对象。
3. 深拷贝(完全不同的对象,但值相同,在堆区开辟内存)
深拷贝会创建一个新的对象,并且递归地复制所有字段和它们的值。如果字段是对象,那么会为这个对象也创建一个拷贝。
特点:
- 创建一个新的对象,并且递归地复制所有字段和它们的值。
- 无论字段是基本类型还是引用类型,都会创建一个新的副本。
4.引用拷贝和浅拷贝在操纵引用类型变量(与原型引用变量的耦合关系)可能引发的问题
由于引用拷贝和浅拷贝在拷贝引用类型变量时仅仅是拷贝了其引用,而未在堆区申请内存,因此与原型变量存在耦合关系,改变一方,二者一起变化
- 修改影响: 如果通过一个引用修改了对象的状态,那么通过另一个引用观察到的对象状态也会发生改变,因为它们指向同一个对象。
- 并非独立: 程序员可能误以为两个引用是独立的,从而在代码中引入错误。
- 内存泄漏: 如果不小心,可能会导致内存泄漏,因为两个引用都指向同一个对象,导致对象无法被垃圾回收。
- 共享内部对象: 对于引用类型的字段,原始对象和浅拷贝对象仍然共享相同的内部对象,修改其中一个对象的引用类型字段会影响到另一个。
5. 案例
定义 Address 类
public class Address {
private String street;
public Address(String street) {
this.street = street;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
}
定义 Person 类
public class Person implements Cloneable{
private String name;
private Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public Object clone() {
//浅拷贝
try {
return super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
public Object deepClone() {
//深拷贝
try {
Person clonedPerson = (Person) super.clone();
clonedPerson.setAddress(new Address(this.getAddress().getStreet()));
return clonedPerson;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
定义主类
public class Main {
static Person original = new Person("Alice", new Address("123 Main St"));
public static void referenceCopy(){
// 引用拷贝
System.out.println("引用拷贝");
Person referenceCopy = original;
System.out.println(original==referenceCopy);
System.out.println(original.getAddress()==referenceCopy.getAddress());
}
public static void shallowCopy(){
// 浅拷贝
System.out.println("浅拷贝");
Person shallowCopy = null;
shallowCopy = (Person) original.clone();
System.out.println(original==shallowCopy);
System.out.println(original.getAddress()==shallowCopy.getAddress());
}
public static void deepCopy(){
// 深拷贝
System.out.println("深拷贝");
Person deepCopy = null;
deepCopy = (Person) original.deepClone();
System.out.println(original==deepCopy);
System.out.println(original.getAddress()==deepCopy.getAddress());
}
public static void main(String[] args) {
referenceCopy();
shallowCopy();
deepCopy();
}
}
结果分析
引用拷贝
original与referenceCopy: true
这意味着 referenceCopy
实际上是指向 original
的同一个对象。也就是说,referenceCopy
和 original
是同一个 Person
对象的不同引用,因此它们的地址是相同的。
original.getAddress()与referenceCopy.getAddress(): true
这表明 referenceCopy
和 original
共享同一个 Address
对象。因此,对其中一个对象的 Address
属性所做的任何更改都会反映到另一个对象上。
浅拷贝
original与shallowCopy: false
这意味着 shallowCopy
和 original
是两个不同的对象,即它们在内存中的地址不同。
original.getAddress()与shallowCopy.getAddress(): true
这表明虽然 shallowCopy
和 original
是不同的 Person
对象,但它们的 Address
属性引用的是同一个 Address
对象。因此,如果修改了 shallowCopy
的 Address
属性,那么 original
的 Address
属性也会受到影响。
深拷贝
original与deepCopy: false
这表明 deepCopy
和 original
是两个不同的 Person
对象。
original.getAddress()与deepCopy.getAddress(): false
这表明 deepCopy
和 original
不仅是不同的 Person
对象,而且它们的 Address
属性也分别指向不同的 Address
对象。因此,对 deepCopy
的 Address
属性所做的任何更改都不会影响到 original
的 Address
属性。
总结
- 引用拷贝 创建了指向同一对象的新引用,所以任何对这个对象的更改都会影响到所有引用该对象的变量。
- 浅拷贝 创建了一个新的顶层对象,但是其中的引用属性仍然指向原有的对象,所以对引用属性的更改会影响所有共享该引用的对象。
- 深拷贝 创建了一个完全独立的新对象,包括所有嵌套的对象,因此对新对象的任何更改都不会影响到原对象。