浅拷贝是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。举例来说更加清楚:对象A1中包含对B1的引用,B1中包含对C1的引用。浅拷贝A1得到A2,A2 中依然包含对B1的引用,B1中依然包含对C1的引用。深拷贝则是对浅拷贝的递归,深拷贝A1得到A2,A2中包含对B2(B1的copy)的引用,B2 中包含对C2(C1的copy)的引用。
接下来通过内存分析来说明深拷贝与浅拷贝的区别,由于本文主要关注两个拷贝的区别,因此在内存分析时只分析堆中的对象
浅拷贝
class House implements Cloneable{
int size;
String address;
public House(int size, String address) {
this.size = size;
this.address = address;
}
public Object clone() throws CloneNotSupportedException{
return (Object)super.clone();
}
}
class Person implements Cloneable{
String name;
int age;
House house;
public Person(String name, int age, House house) {
this.name = name;
this.age = age;
this.house = house;
}
public Person clone() {
Person o = null;
try {
o = (Person)super.clone();
}catch(Exception e) {
System.out.println(e);
}
return o;
}
}
public class CloneTest {
public static void main(String[] args) {
House house1 = new House(50, "平凡花苑");
Person person1 = new Person("Xiaoming", 20, house1);
System.out.println("改变前:" + "person1.name:" + person1.name + " person1.age:" + person1.age + " person1.house.size:" + person1.house.size + " person1.house.address:" + person1.house.address);
Person person2 = person1.clone();
person2.name = "Meimei";
person2.age = 30;
//标记处
person2.house.size = 80;
person2.house.address = "梦想花苑";
System.out.println("改变后:" + "person1.name:" + person1.name + " person1.age:" + person1.age + " person1.house.size:" + person1.house.size + " person1.house.address:" + person1.house.address);
}
}
// 改变前:person1.name:Xiaoming person1.age:20 person1.house.size:50 person1.house.address:平凡花苑
// 改变后:person1.name:Xiaoming person1.age:20 person1.house.size:80 person1.house.address:梦想花苑
标记处前
House house1 = new House(50, "平凡花苑");
以Ox开始的表示对象在堆中的地址,首先new出一个house1对象,由于house对象的属性都是基本类型,因此没有指向别的对象的箭头
Person person1 = new Person("Xiaoming", 20, house1);
创建person1时,由于该对象的成员变量中有一个是引用变量,因此person1中保存着house1的地址(因此修改house1的成员变量时实际是在person1变量外完成的)
Person person2 = person1.clone();
通过person1.clone()方法创建的person2拥有person1所有的基本类型的成员变量,由于Object.clone方法是一种浅拷贝的方法,因此无法同时拷贝引用类型的成员变量,因此在person2中的house还是保存着原来的house1的内存地址
person2.name = "Meimei";
person2.age = 30;
person2中的普通类型变量已经完全脱离person1,因此上述修改后person2.name和person2.age都会改变
标记处后
person2.house.size = 80;
person2.house.address = "梦想花苑";
此时person1和person2指向同一个house1,因此修改person2中house1的成员变量,person1中house1的成员变量也会随之改变
深拷贝
class House implements Cloneable{
int size;
String address;
public House(int size, String address) {
this.size = size;
this.address = address;
}
public Object clone() throws CloneNotSupportedException{
return (Object)super.clone();
}
}
class Person implements Cloneable{
String name;
int age;
House house;
public Person(String name, int age, House house) {
this.name = name;
this.age = age;
this.house = house;
}
public Person clone() throws CloneNotSupportedException { //改变
Person o = null;
try {
o = (Person)super.clone();
}catch(Exception e) {
System.out.println(e);
}
o.house = (House) house.clone(); //主要改变
return o;
}
}
public class CloneTest {
public static void main(String[] args) throws CloneNotSupportedException { //改变
House house1 = new House(50, "平凡花苑");
Person person1 = new Person("Xiaoming", 20, house1);
System.out.println("改变前:" + "person1.name:" + person1.name + " person1.age:" + person1.age + " person1.house.size:" + person1.house.size + " person1.house.address:" + person1.house.address);
Person person2 = person1.clone();
person2.name = "Meimei";
person2.age = 30;
//标记处
person2.house.size = 80;
person2.house.address = "梦想花苑";
System.out.println("改变后:" + "person1.name:" + person1.name + " person1.age:" + person1.age + " person1.house.size:" + person1.house.size + " person1.house.address:" + person1.house.address);
}
}
// 改变前:person1.name:Xiaoming person1.age:20 person1.house.size:50 person1.house.address:平凡花苑
// 改变后:person1.name:Xiaoming person1.age:20 person1.house.size:50 person1.house.address:平凡花苑
标记处前
House house1 = new House(50, "平凡花苑");
Person person1 = new Person("Xiaoming", 20, house1);
Person person2 = person1.clone();
person2.name = "Meimei";
person2.age = 30;
上述代码和浅拷贝基本一样,因此不过多描述,主要观察下面不同点
o.house = (House) house.clone(); //主要改变
与浅拷贝相比,深拷贝多了上述一行代码,和person1.clone()类似,由于house中只有基本类型的变量,因此house.clone可以完全拷贝,此时得到的house2与house1没有任何关系
标记处后
person2.house.size = 80;
person2.house.address = "梦想花苑";
由于person2和其指向的house2与原型(person1和house1)没有任何关系,此时对 house的任何操作都不会影响person1中的house1
拓展
通过以上介绍,大家应该能理解深拷贝和浅拷贝的区别。本质上来说,深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象,也就是说深拷贝得到的两个对象没有任何交集。为了加深印象,我们继续引入一个tv变量,该变量是house1中的一个引用类型的变量,通过该变量,person1和person2再次建立联系
class House implements Cloneable{
int size;
String address;
TV tv; //增加一个tv类
public House(int size, String address, TV tv) { //改变构造方法
this.size = size;
this.address = address;
this.tv = tv;
}
public Object clone() throws CloneNotSupportedException{
return (Object)super.clone();
}
}
//增加一个TV类
class TV{
int size;
String brand;
public TV(int size, String brand) {
this.size = size;
this.brand = brand;
}
}
class Person implements Cloneable{
String name;
int age;
House house;
public Person(String name, int age, House house) {
this.name = name;
this.age = age;
this.house = house;
}
public Person clone() throws CloneNotSupportedException { //改变
Person o = null;
try {
o = (Person)super.clone();
}catch(Exception e) {
System.out.println(e);
}
o.house = (House) house.clone();
return o;
}
}
public class CloneTest {
public static void main(String[] args) throws CloneNotSupportedException { //改变
TV tv = new TV(65, "SONY"); //添加一个tv对象
House house1 = new House(50, "平凡花苑", tv); //改变
Person person1 = new Person("Xiaoming", 20, house1);
System.out.println("改变前:" + "person1.name:" + person1.name + " person1.age:" + person1.age + " person1.house.size:" + person1.house.size + " person1.house.address:" + person1.house.address + " person1.house.tv.size:" + person1.house.tv.size + " person1.house.tv.brand:" + person1.house.tv.brand); //改变
Person person2 = person1.clone();
person2.name = "Meimei";
person2.age = 30;
person2.house.size = 80;
person2.house.address = "梦想花苑";
//标记处
person2.house.tv.size = 90; //添加
person2.house.tv.brand = "Xiaomi"; //添加
System.out.println("改变后:" + "person1.name:" + person1.name + " person1.age:" + person1.age + " person1.house.size:" + person1.house.size + " person1.house.address:" + person1.house.address + " person1.house.tv.size:" + person1.house.tv.size + " person1.house.tv.brand:" + person1.house.tv.brand);
}
}
// 改变前:person1.name:Xiaoming person1.age:20 person1.house.size:50 person1.house.address:平凡花苑 person1.house.tv.size:65 person1.house.tv.brand:SONY
// 改变后:person1.name:Xiaoming person1.age:20 person1.house.size:50 person1.house.address:平凡花苑 person1.house.tv.size:90 person1.house.tv.brand:Xiaomi
标记处前
o.house = (House) house.clone();
由于有这行代码,house2会浅拷贝house1中所有的普通类型变量,而由于tv是一个引用类型变量,而在拷贝house1过程中没有拷贝引用类型变量,因此house1和house2都指向同一地址的tv
标记处后
person2.house.tv.size = 90; //添加
person2.house.tv.brand = "Xiaomi"; //添加
到这也应该很容易的理解通过上述改变后,house1和house2中的tv属性都一起改变了
o.tv = (TV) TV.clone();
假如在house类中加入形如上述的代码,则结果又不一样
通过上述三个实验,总结一下,要想实现深拷贝,只要递归拷贝所有拥有引用类型变量的对象,直到两个最外层两个对象没有任何关系。而造成深浅拷贝的原因在于对象中保存引用类型变量时只保存相应的堆地址(Ox开头的),而Object.clone()方法只能实现浅拷贝