JAVA中深拷贝与浅拷贝(在网上找到的) 希望对于理解深拷贝与浅拷贝有帮助...
?
?什么是clone?
??????? 在實際編程過程中,我們常常要遇到這種情況:有一個對象A,在某一時刻A中已經包含了一些有效值,此時可能會需要一個和A完全相同新對象B,并且此后對B任何改動都不會影響到A中的值,也就是說,A與B是兩個獨立的對象,但B的初始值是由A對象確定的。在Java語言中,用簡單的賦值語句是不能滿足這種需求的。要滿足這種需求雖然有很多途徑,但實現clone()方法是其中最簡單,也是最高效的手段。
??????? Java的所有類都默認繼承java.lang.Object類,在java.lang.Object類中有一個方法clone()。JDK API的說明文檔解釋這個方法將返回Object對象的一個拷貝。要說明的有兩點:一是拷貝對象返回的是一個新對象,而不是一個引用。二是拷貝對象與用new操作符返回的新對象的區別就是這個拷貝已經包含了一些原來對象的信息,而不是對象的初始信息。
怎樣應用clone()方法?
???? 一個很典型的調用clone()代碼如下:
class CloneClass implements Cloneable{public int aInt;public Object clone(){CloneClass o = null;try{o = (CloneClass)super.clone();}catch(CloneNotSupportedException e){e.printStackTrace();}return o;} }?有三個值得注意的地方,一是希望能實現clone功能的CloneClass類實現了Cloneable接口,這個接口屬于java.lang包,java.lang包已經被缺省的導入類中,所以不需要寫成java.lang.Cloneable。另一個值得請注意的是重載了clone()方法。最后在clone()方法中調用了super.clone(),這也意味著無論clone類的繼承結構是什么樣的,super.clone()直接或間接調用了java.lang.Object類的clone()方法。下面再詳細的解釋一下這幾點。
?
應該說第三點是最重要的,仔細觀察一下Object類的clone()一個native方法,native方法的效率一般來說都是遠高于java中的非native方法。這也解釋了為什么要用Object中clone()方法而不是先new一個類,然后把原始對象中的信息賦到新對象中,雖然這也實現了clone功能。對于第二點,也要觀察Object類中的clone()還是一個protected屬性的方法。這也意味著如果要應用clone()方法,必須繼承Object類,在Java中所有的類是缺省繼承Object類的,也就不用關心這點了。然后重載clone()方法。還有一點要考慮的是為了讓其它類能調用這個clone類的clone()方法,重載之后要把clone()方法的屬性設置為public。
那么clone類為什么還要實現Cloneable接口呢?稍微注意一下,Cloneable接口是不包含任何方法的!其實這個接口僅僅是一個標志,而且這個標志也僅僅是針對Object類中clone()方法的,如果clone類沒有實現Cloneable接口,并調用了Object的clone()方法(也就是調用了super.Clone()方法),那么Object的clone()方法就會拋出CloneNotSupportedException異常。
?
注意:1.所有的數組都被視為實現接口 Cloneable。
?? ? ?2.Object 類本身不實現接口 Cloneable,所以在類為 Object 的對象上調用 clone 方法將會導致在運行時拋出異常。
以上是clone的最基本的步驟,想要完成一個成功的clone,還要了解什么是"影子clone"和"深度clone"。
?
什么是影子clone?
下面的例子包含三個類UnCloneA,CloneB,CloneMain。CloneB類包含了一個UnCloneA的實例和一個int類型變量,并且重載clone()方法。CloneMain類初始化UnCloneA類的一個實例b1,然后調用clone()方法生成了一個b1的拷貝b2。最后考察一下b1和b2的輸出:
package clone; class UnCloneA {private int i;public UnCloneA(int ii) { i = ii; }public void doubleValue() { i *= 2; }public String toString() {return Integer.toString(i);} } class CloneB implements Cloneable{public int aInt;public UnCloneA unCA = new UnCloneA(111);public Object clone(){CloneB o = null;try{o = (CloneB)super.clone();}catch(CloneNotSupportedException e){e.printStackTrace();}return o;} } public class CloneMain {public static void main(String[] a){CloneB b1 = new CloneB();b1.aInt = 11;System.out.println("before clone,b1.aInt = "+ b1.aInt);System.out.println("before clone,b1.unCA = "+ b1.unCA);CloneB b2 = (CloneB)b1.clone();b2.aInt = 22;b2.unCA.doubleValue();System.out.println("=================================");System.out.println("after clone,b1.aInt = "+ b1.aInt);System.out.println("after clone,b1.unCA = "+ b1.unCA);System.out.println("=================================");System.out.println("after clone,b2.aInt = "+ b2.aInt);System.out.println("after clone,b2.unCA = "+ b2.unCA);} }/** RUN RESULT: before clone,b1.aInt = 11 before clone,b1.unCA = 111 ================================= after clone,b1.aInt = 11 after clone,b1.unCA = 222 ================================= after clone,b2.aInt = 22 after clone,b2.unCA = 222 */輸出的結果說明int類型的變量aInt和UnCloneA的實例對象unCA的clone結果不一致,int類型是真正的被clone了,因為改變了b2中的aInt變量,對b1的aInt沒有產生影響,也就是說,b2.aInt與b1.aInt已經占據了不同的內存空間,b2.aInt是b1.aInt的一個真正拷貝。相反,對b2.unCA的改變同時改變了b1.unCA,很明顯,b2.unCA和b1.unCA是僅僅指向同一個對象的不同引用!從中可以看出,調用Object類中clone()方法產生的效果是:先在內存中開辟一塊和原始對象一樣的空間,然后原樣拷貝原始對象中的內容。對基本數據類型,這樣的操作是沒有問題的,但對非基本類型變量,我們知道它們保存的僅僅是對象的引用,這也導致clone后的非基本類型變量和原始對象中相應的變量指向的是同一個對象。
大多時候,這種clone的結果往往不是我們所希望的結果,這種clone也被稱為"影子clone"。要想讓b2.unCA指向與b2.unCA不同的對象,而且b2.unCA中還要包含b1.unCA中的信息作為初始信息,就要實現深度clone。
?
怎么進行深度clone?
把上面的例子改成深度clone很簡單,需要兩個改變:一是讓UnCloneA類也實現和CloneB類一樣的clone功能(實現Cloneable接口,重載clone()方法)。二是在CloneB的clone()方法中加入一句o.unCA = (UnCloneA)unCA.clone();
程序如下:
package clone.ext; class UnCloneA implements Cloneable{private int i;public UnCloneA(int ii) { i = ii; }public void doubleValue() { i *= 2; }public String toString() {return Integer.toString(i);}public Object clone(){UnCloneA o = null;try{o = (UnCloneA)super.clone();}catch(CloneNotSupportedException e){e.printStackTrace();}return o;} } class CloneB implements Cloneable{public int aInt;public UnCloneA unCA = new UnCloneA(111);public Object clone(){CloneB o = null;try{o = (CloneB)super.clone();}catch(CloneNotSupportedException e){e.printStackTrace();}o.unCA = (UnCloneA)unCA.clone();return o;} } public class CloneMain {public static void main(String[] a){CloneB b1 = new CloneB();b1.aInt = 11;System.out.println("before clone,b1.aInt = "+ b1.aInt);System.out.println("before clone,b1.unCA = "+ b1.unCA);CloneB b2 = (CloneB)b1.clone();b2.aInt = 22;b2.unCA.doubleValue();System.out.println("=================================");System.out.println("after clone,b1.aInt = "+ b1.aInt);System.out.println("after clone,b1.unCA = "+ b1.unCA);System.out.println("=================================");System.out.println("after clone,b2.aInt = "+ b2.aInt);System.out.println("after clone,b2.unCA = "+ b2.unCA);} } /** RUN RESULT: before clone,b1.aInt = 11 before clone,b1.unCA = 111 ================================= after clone,b1.aInt = 11 after clone,b1.unCA = 111 ================================= after clone,b2.aInt = 22 after clone,b2.unCA = 222 */可以看出,現在b2.unCA的改變對b1.unCA沒有產生影響。此時b1.unCA與b2.unCA指向了兩個不同的UnCloneA實例,而且在CloneB b2 = (CloneB)b1.clone();調用的那一刻b1和b2擁有相同的值,在這里,b1.i = b2.i = 11。
要知道不是所有的類都能實現深度clone的。例如,如果把上面的CloneB類中的UnCloneA類型變量改成StringBuffer類型,看一下JDK API中關于StringBuffer的說明,StringBuffer沒有重載clone()方法,更為嚴重的是StringBuffer還是一個final類,這也是說我們也不能用繼承的辦法間接實現StringBuffer的clone。如果一個類中包含有StringBuffer類型對象或和StringBuffer相似類的對象,我們有兩種選擇:要么只能實現影子clone,要么就在類的clone()方法中加一句(假設是SringBuffer對象,而且變量名仍是unCA): o.unCA = new StringBuffer(unCA.toString()); //原來的是:o.unCA = (UnCloneA)unCA.clone();
還要知道的是除了基本數據類型能自動實現深度clone以外,String對象是一個例外,它clone后的表現好象也實現了深度clone,雖然這只是一個假象,但卻大大方便了我們的編程。
?
來自:http://blog.csdn.net/shimeile19881217/archive/2008/04/06/2254809.aspx
轉載于:https://www.cnblogs.com/afirefly/archive/2010/09/08/1821810.html
總結
以上是生活随笔為你收集整理的JAVA中深拷贝与浅拷贝(在网上找到的) 希望对于理解深拷贝与浅拷贝有帮助...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SilverLight入门实例(一)
- 下一篇: 小技巧、小工具列表