天天看點

java對象克隆

1. Overiew

在實際程式設計中,我們經常會遇到這樣一個情景:有一個對象A,存在屬性方法,現在需要一個和A完全相同的新對象B,并且B的任何改動都不會影響到A中的值。那麼,最常用的辦法就是對A進行克隆。

2. How to Clone

在java.lang.Object中有一個clone方法,該方法的簽名如下:

Java代碼

 protected native Object clone() throws CloneNotSupportedException;  

 該方法傳回一個Object執行個體的拷貝,該執行個體拷貝具有:

1)拷貝對象是一個新對象而不是一個原對象引用。

2)拷貝的對象中包含的是原有對象資訊,而不是對象初始化的資訊(也就是說,拷貝過程沒有調用構造函數)。

從第1條我們可以看出,拷貝的對象與原對象存在這樣一種關系:

1. x.clone() != x      will be true    

2. x.clone().getClass() == x.getClass() will be true, but these are not absolute requirements        

3. x.clone().equals(x) will be true, but these are not absolute requirements    

要實作克隆,必須滿足一下三點:

1)類必須實作Cloneable接口

2)類必須重載Object類中的clone()方法

3)重載的clone()方法中,必須顯示調用super.clone()。

我們看一下Cloneable接口的定義:

 public interface Cloneable { }   

Cloneable接口是不包含任何方法的,僅僅表示一個标志(類似Serializable接口),而且這個标志也是針對Object類中clone()方法的,如果clone類沒有實作Cloneable接口,并調用了Object的clone()方法(也就是調用了super.Clone()方法),那麼Object的clone()方法就會抛出CloneNotSupportedException異常。

按照要求,我們寫一個克隆類的小例子:

3. Shadow Clone

首先,我們來看一個例子:ClassA沒有實作Cloneable接口。ClassB存在ClassA的執行個體,實作了Cloneable接口,并重載了clone方法。ClassC執行個體化一個ClassB對象,然後克隆一個對象。

輸出結果如下:

before clone : cb.b = 222    

before clone : cb.ca = 11    

========================     

after clone : cb.b = 222    

after clone : cb.ca = 22    

after clone : cb2.b = 3333    

after clone : cb2.ca = 22 

從結果我們看到,int類型的b被完全的克隆了,而ClassA類型的ca卻沒有被克隆。因為克隆後cb2對b的指派不會影響原有cb中b的值,而調用cb2.ca.doubleA()方法後,對cb2.ca的改變同時改變了cb.ca,表明cb2.ca與cb.ca僅僅指向同一個對象的不同引用。從中可以看出,調用Object類中clone()方法産生的效果是:先在記憶體中開辟一塊和原始對象一樣的空間,然後原樣拷貝原始對象中的内容。對基本資料類型,這樣的操作是沒有問題的,但對非基本類型變量clone後的變量和原始對象中相應的變量指向的是同一個對象。

這就是影子克隆。影子克隆,并沒有完整的完成克隆,有時候這并不是我們想要的結果。我們有時需要,就像列子中調用cb2.ca.doubleA()方法時,不會對原對象産生改變,這時候我們就需要deep clone;

4. Deep Clone

要實作深度克隆,在上面的例子基礎上,我們隻需要這樣修改即可:

輸出結果:

基本的資料類型可以自動的實作深度的克隆,然而并不是所有的類都可以進行深度的克隆,就比如String類,其類定義為final,且沒有重載clone方法,我們就無法完成對String類的深度克隆。關于不可變對象的克隆,我們通常沒有必要去克隆。

自己寫的克隆:

是這樣的.object的clone()方法是protected。如果我們在測試類直接調用會報錯:The method clone() from the type Object is not visible

因為他對Object.clone()進行了多态性的調用,而這個方法被聲明是受保護的。

我們需要在Employee3類中添加clone()方法的一個隻通路覆寫版本。隻通路覆寫版本沒有添加邏輯,他隻是放松了對你的代碼的通路限制public。記住覆寫的方法的通路限制不能比基類方法更嚴格,但是可以更寬松。

輸出:

clone.Employee3@c17164

the employee's name is Dolly

the employee's salary is 1000

clone.Employee3@1fb8ee3 與前面的不同,說明不是同一個對象

via:http://developer.51cto.com/art/201105/260821.htm

http://liuwang126.iteye.com/blog/272595

繼續閱讀