在《Java編程思想》第16章數組部分,提到了System.arraycopy()對基本類型數組與對象數組的淺復制(參考P451),因此具體的來學習一下深拷貝與淺拷貝。
淺拷貝(Shallow Copy):
1. 對於基本數據類型,淺拷貝會直接進行值傳遞,也就是將該屬性值復制一份給新的對象。因為是兩份不同的數據,所以對其中一個對象的該成員變量值進行修改,不會影響另一個對象拷貝得到的數據。
2. 對於引用類型,比如說成員變量是某個數組、某個類的對象等,那么淺拷貝會進行引用傳遞,也就是只是將該成員變量的引用值(內存地址)復制一份給新的對象。因為實際上兩個對象的該成員變量都指向同一個實例。在這種情況下,在一個對象中修改該成員變量會影響到另一個對象的該成員變量值。
深拷貝(deep copy):
1. 對於深拷貝來說,不僅要復制對象的所有基本數據類型,還要為所有引用類型的成員變量申請存儲空間,並復制每個引用類型所引用的對象,直到該對象可達的所有對象。
(一)淺拷貝的實現方法
1. 引用賦值;
2. 重寫clone方法:
Object類是類結構的根類,其中有一個進行淺拷貝的方法:
protected Object clone() throws CloneNotSupportedException
有了這個淺拷貝模板,我們可以通過調用clone()方法來實現對象的淺拷貝。但是需要注意:
i) Object類雖然有這個方法,但是這個方法是受保護的(被protected修飾),無法直接使用。因此使用clone方法的類必須實現Cloneable接口,否則會拋出異常CloneNotSupportedException。
ii) 使用的基本形式:
class A implements Cloneable { public Object clone() { Object obj = null; try { obj = super.clone(); } catch (CloneNotSupportedException e) { } return obj; } }
這一奇怪的用法《Java編程思想》的作者嘗試給出了解釋:Java剛開始作為一種驅動硬件的語言出現,那時候Object類有個方法是public Object clone();隨着Java被用到網絡編程中,clone方法的安全性受到質疑,因此設計者將public類型的clone方法修改為protected類型;但即便這樣,仍然有安全問題,因為子類可能濫用父類的clone方法,因為它是protected類型的。為此,Java設計者又設計了一個Cloneable接口,當子類執行super.clone()語句時,會先核對執行該語句的子類對象是否實現了Cloneabe接口,以便確認開發者需要打開該子類的克隆功能,這就能最大程度的保證安全性。 |
(二)深拷貝的實習方法
1. 對每一個引用對象重寫clone方法
與通過重寫clone方法實現淺拷貝的基本思路一樣,只需要為對象圖的每一層的每一個對象都實現Cloneable接口並重寫clone方法,最后在最頂層的類的重寫的clone方法中調用所有的clone方法即可實現深拷貝。簡單的說就是:每一層的每個對象都進行淺拷貝=深拷貝。
2. 通過對象序列化實現深拷貝(推薦使用)
將對象序列化為字節序列時,默認會將該對象的引用對象進行序列化,再通過反序列即可完美地實現深拷貝。