參考:
Java核心技術
https://blog.csdn.net/zhangjg_blog/article/details/18369201
https://www.jianshu.com/p/94dbef2de298
基本數據類型的拷貝
Object類中的clone()方法實現的是淺拷貝,對於基本數據類型,由於是直接複製值,原對象和克隆對象(副本)之間狀態各自獨立,不會相互影響。如果對象中所有的數據域都是基本數據類型,淺拷貝這些域沒有任何問題。因爲此時,淺拷貝和深拷貝之間不存在差別。
引用數據類型的拷貝
但是如果原對象包含子對象(即另一個類的對象)的引用,淺拷貝對應域就會得到相同子對象的另一個引用。也就是說,淺拷貝在該屬性上並沒有複製子對象的狀態(內容),而只是複製了引用的值。這樣一來,原對象和克隆的對象仍然會共享一些信息。這可能就會造成,當我們對其中一個引用數據類型所指向的對象的狀態(內容)進行改變的時候,另一個複製版本也會受到改動的影響。當然如果共享的子對象是不可變的,那麼這種共享就是安全的,但通常子對象都是可變的。爲了避免出現這種情況,就必須重新定義clone實現深拷貝。
深拷貝和淺拷貝
通過前面的講述,我們可以看出,淺拷貝和深拷貝的特點。
淺拷貝
如果屬性是基本數據類型,淺拷貝得到的就是基本數據類型的值;如果屬性是內存地址(引用類型,稱爲子對象),淺拷貝得到的就是子對象的另一個引用 。因此對於原對象和克隆對象的子對象,2者引用類型的數據仍指向同一個內存空間。如果其中一個對象改變了這個地址,就會影響到另一個對象。
示例
借用Java核心技術中的例子來解釋淺拷貝,Employee類的屬性如下。
public class Employee {
String name;
double salary;
Date hireday;
}
由於name和hireDay是引用類型數據,所以在進行淺拷貝之後,原對象和克隆對象中對應的屬性都指向同一塊內存存儲空間。
深拷貝
深拷貝和淺拷貝唯一的不同就在於對引用類型數據的處理上。淺拷貝得到的是子對象的另一個引用,而深拷貝的得到一個存放了子對象狀態的新的空間。
示例
深拷貝的結果就如下。原對象和克隆對象所有的屬性都存在獨立的空間中,2者不存在共享的存儲空間。
如何進行深拷貝
如果想要深拷貝一個對象,這個對象所屬的類必須實現Cloneable接口,並重寫clone方法,而且在clone方法的內部,把該對象引用的其他對象(即子對象)也要clone一份,這就要求那些子對象所屬的類也必須要實現Cloneable接口並且實現clone方法。值得注意的是,Cloneable接口只是一個標記接口,而標記接口並不包含任何方法。事實上,clone方法存在於Object類中。
實現深拷貝的示例
主函數
package ch6;
public class CopyTest {
public static void main(String[] args) {
Person p1 = new Person("zhao",20,"上海市靜安區","241001");
System.out.println("修改前p1 = "+p1);
// 實現深拷貝
Person p1copy = p1.clone();
// 修改克隆對象的狀態
p1copy.getAddr().setAddress("合肥市濱湖新區");
System.out.println("p1copy修改後p1 = "+p1);
System.out.println("p1copy = "+p1copy);
}
}
Person類的定義
package ch6;
public class Person implements Cloneable{
private String name;
private int age;
private Address addr; // 引用數據類型
public Person(){};
public Person(String name,int age,String address,String postcode)
{
this.name = name;
this.age = age;
addr = new Address(address,postcode);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Address getAddr() {
return addr;
}
public void setAddr(Address addr) {
this.addr = addr;
}
@Override
public String toString() {
return "Person["+
"name : "+name
+",age :"+age+
","+addr.toString()+" ]";
}
@Override
public Person clone() {
try {
//
Person cloned = (Person)super.clone();
// 注意1:拷貝引用數據類型(如果不重寫Address類中的clone方法則下面語句報編譯錯)
// the method clone() from type Object is not visible
cloned.addr = (Address)addr.clone();
return cloned;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
Address類的定義
package ch6;
public class Address implements Cloneable{
private String address;
private String postcode;
public Address(){}
public Address(String address,String postcode)
{
this.address = address;
this.postcode = postcode;
}
public void setAddress(String address) {
this.address = address;
}
public void setPostcode(String postcode) {
this.postcode = postcode;
}
@Override
public String toString() {
return "address ["
+address+","+postcode+"]";
}
@Override
public Address clone() {
try {
return (Address)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}