基礎 | Java的深拷貝與淺拷貝
Object類中的clone()方法定義如下:
/**
* 僅對本包下的所有類和當前類的子類可見。
* 只有實現Cloneable介面的類的物件才能呼叫該方法,否則會丟擲異常
*/
protected native Object clone() throws CloneNotSupportedException;
該方法本質上是一種對物件的「淺拷貝」,那麼 「淺拷貝與深拷貝分別是什麼?兩者有什麼區別?如何實現?」,在此進行一個系統的總結和梳理。
參考答案
淺拷貝和深拷貝有什麼區別?
先來了解一下兩個概念:「引用拷貝」和「物件拷貝」。
「引用拷貝」是指建立一個指向物件的引用變數的拷貝,例如:
Employee emp1 = new Employee("Taylor", 26);
Employee emp2 = emp1;
System.out.println(emp1); // [email protected]
System.out.println(emp2); // [email protected]
即emp1和emp2指向堆空間中的同一個物件,這就叫「引用拷貝」。
而「物件拷貝」是指建立物件本身的一個副本,例如:
Employee emp1 = new Employee("Swift", 26);
Employee emp2 = (Employee) emp1.clone();
System.out.println(emp1); // [email protected]
System.out.println(emp2); // [email protected]
即emp1和emp2分別指向堆空間中的不同物件,這就叫「物件拷貝」,但需要注意的是,使用clone()方法進行物件拷貝時,必須要求Employee類實現Cloneable介面並重寫clone()方法,且上述程式碼段所在的方法還需要處理CloneNotSupportedException異常。
其中,「淺拷貝」和「深拷貝」都屬於「物件拷貝」。
對於基本資料型別的成員變數,無論「淺拷貝」還是「深拷貝」都會直接進行值傳遞,原物件和新物件的該屬性值為兩份資料,相互獨立,且互不影響。
而對於引用型別的成員變數,「淺拷貝」僅複製該屬性的引用而不復制引用所指向的物件,即原物件和新物件的該屬性指向的是同一個物件;「深拷貝」則會直接複製該屬性所指向的物件,即原物件和新物件的該屬性指向的是堆空間中記憶體地址不同的物件。
如何實現「淺拷貝」?
要求待拷貝物件所屬類:
- 實現Cloneable介面;
- 重寫clone()方法,並指定public訪問修飾符。
要求在呼叫待拷貝物件的clone()方法時:
- 處理編譯時異常:CloneNotSupportedException。
另外,也可以手動採用賦值的方式將原物件的各個屬性值拷貝到新的物件。
如何實現「深拷貝」?
方式1:通過實現Cloneable介面並重寫clone()方法來實現,即將原物件及其所引用的所有物件所屬的類均實現Cloneable介面並重寫clone()方法。
方式2:通過序列化方式來實現。由於篇幅有限,具體實現暫且先埋一個坑吧!
Object類的clone()方法為什麼要宣告為protected?
若宣告為public的,則在子類不重寫clone()方法時,呼叫的還是Object類的clone()方法,只能實現淺拷貝。而宣告為protected的,就要求子類在需要拷貝物件時,必須要要實現Cloneable介面並重寫clone()方法,在其中既可實現淺拷貝也可實現深拷貝,但通常都需要實現深拷貝。
原始碼閱讀
// 需要拷貝類的例項時,必須實現Cloneable介面,重寫clone()方法並將其宣告為public的
public class Employee implements Cloneable {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Employee(String name, Integer age) {
super();
this.name = name;
this.age = age;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
推薦閱讀
歡迎關注
Java名企面試吧,每天10點24分,我們不見不散!
丙子先生的宗旨是,每天以短篇幅講高頻面試題,不增加太多負擔,但需要持之以恆。
能力有限,歡迎指教!