天天看點

java中的深淺克隆

假設有一個對象object,在某處又需要一個跟object一樣的執行個體object2,強調的是object和object2是兩個獨立的執行個體,

隻是在開始的時候,他們是具有相同狀态的(屬性字段的值都相同)。遇到這種情況的做法一般是,重新new一個對象object2,将object的字段值

賦予object2,即:object2=object; 這樣的話兩個引用仍然指向的是同一個對象,不是兩個對象。

java中跟克隆有關的兩個類分别是cloneable接口和object類中的clone方法,通過兩者的協作來實作克隆。

首先來看看object的clone()源代碼:

首先看一下java api doc中關于cloneable接口和object類中的clone方法的描述:

java.lang.cloneable 接口(以下源引javatm 2 platform standard ed. 5.0 api doc) 此類實作了 cloneable 接口,以訓示 object.clone() 方法可以合法地對該類執行個體進行按字段複制。 如果在沒有實作 cloneable 接口的執行個體上調用 object 的 clone 方法,則會導緻抛出 clonenotsupportedexception異常。  按照慣例,實作此接口的類應該使用公共方法重寫 object.clone(它是受保護的)。請參閱 object.clone(),以獲得有關重寫此方法的詳細資訊。  注意,此接口不包含 clone 方法。是以,因為某個對象實作了此接口就克隆它是不可能的。即使 clone 方法是反射性調用的,也無法保證它将獲得成功。

cloneable接口沒有任何方法,僅是個标志接口(tagging interface),若要具有克隆能力,實作cloneable接口的類必須重寫從object繼承來的clone方法,并調用object的clone方法(見下面object#clone的定義),重寫後的方法應為public 的。

clone方法首先會判對象是否實作了cloneable接口,若無則抛出clonenotsupportedexception,

最後會調用internalclone.

intervalclone是一個native方法,一般來說native方法的執行效率高于非native方法。

當某個類要複寫clone方法時,要繼承cloneable接口。通常的克隆對象都是通過super.clone()方法來克隆對象。

   克隆就是複制一個對象的複本,若隻需要複制對象的字段值(對于基本資料類型,如:int,long,float等,則複制值;對于複合資料類型僅複制該字段值,如數組變量則複制位址,對于對象變量則複制對象的reference。

舉個例子:

測試代碼如下:

運作結果:

克隆前c1:  a=100 b=1000

克隆前c1:  a=100 b=5

克隆後c2:  a=50 b[0]=5

c1和c2的對象模型:

java中的深淺克隆

可以看出,基本類型可以使用淺克隆,而對于引用類型,由于引用的是内容相同,是以改變c2執行個體對象中的屬性就會影響到c1。是以引用類型需要使用深克隆。另外,在開發一個不可變類的時候,如果這個不可變類中成員有引用類型,則就需要通過深克隆來達到不可變的目的。

深克隆與淺克隆的差別在于對複合資料類型的複制。若對象中的某個字段為複合類型,在克隆對象的時候,需要為該字段重新建立一個對象。

再舉一個例子:

對象模型:

java中的深淺克隆

然後編寫測試類:

運作後的結果如下:

克隆前dc1: a=100 b[0]=1000

克隆後dc1: a=100 b[0]=1000

克隆後dc2: a=50 b[0]=500

可以看到,兩個引用所指向的對象在堆中互相獨立,互不幹擾,這樣就實作了深度克隆。

1、克隆方法用于建立對象的拷貝,為了使用clone方法,類必須實作java.lang.cloneable接口重寫protected方法clone,如果沒有實作clonebale接口會抛出clonenotsupportedexception.

2、在克隆java對象的時候不會調用構造器

3、java提供一種叫淺拷貝(shallow

copy)的預設方式實作clone,建立好對象的副本後然後通過指派拷貝内容,意味着如果你的類包含引用類型,那麼原始對象和克隆都将指向相同的引用内

容,這是很危險的,因為發生在可變的字段上任何改變将反應到他們所引用的共同内容上。為了避免這種情況,需要對引用的内容進行深度克隆。

4、按照約定,執行個體的克隆應該通過調用super.clone()擷取,這樣有助克隆對象的不變性。如:clone!=original和clone.getclass()==original.getclass(),盡管這些不是必須的