影子clone和深度clone
什么是影子clone?
下面的例子包含三個(gè)類UnCloneA,CloneB,CloneMain。CloneB類包含了一個(gè)UnCloneA的實(shí)例和一個(gè)int類型變量,并且重載clone()方法。CloneMain類初始化UnCloneA類的一個(gè)實(shí)例b1,然后調(diào)用clone()方法生成了一個(gè)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
*/
|
輸出的結(jié)果說明int類型的變量aInt和UnCloneA的實(shí)例對象unCA的clone結(jié)果不一致,int類型是真正的被clone了,因?yàn)楦淖兞薭2中的aInt變量,對b1的aInt沒有產(chǎn)生影響,也就是說,b2.aInt與b1.aInt已經(jīng)占據(jù)了不同的內(nèi)存空間,b2.aInt是b1.aInt的一個(gè)真正拷貝。相反,對b2.unCA的改變同時(shí)改變了b1.unCA,很明顯,b2.unCA和b1.unCA是僅僅指向同一個(gè)對象的不同引用!從中可以看出,調(diào)用Object類中clone()方法產(chǎn)生的效果是:先在內(nèi)存中開辟一塊和原始對象一樣的空間,然后原樣拷貝原始對象中的內(nèi)容。對基本數(shù)據(jù)類型,這樣的操作是沒有問題的,但對非基本類型變量,我們知道它們保存的僅僅是對象的引用,這也導(dǎo)致clone后的非基本類型變量和原始對象中相應(yīng)的變量指向的是同一個(gè)對象。
大多時(shí)候,這種clone的結(jié)果往往不是我們所希望的結(jié)果,這種clone也被稱為"影子clone"。要想讓b2.unCA指向與b2.unCA不同的對象,而且b2.unCA中還要包含b1.unCA中的信息作為初始信息,就要實(shí)現(xiàn)深度clone。
怎么進(jìn)行深度clone?
把上面的例子改成深度clone很簡單,需要兩個(gè)改變:一是讓UnCloneA類也實(shí)現(xiàn)和CloneB類一樣的clone功能(實(shí)現(xiàn)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
*/
|
可以看出,現(xiàn)在b2.unCA的改變對b1.unCA沒有產(chǎn)生影響。此時(shí)b1.unCA與b2.unCA指向了兩個(gè)不同的UnCloneA實(shí)例,而且在CloneB b2 = (CloneB)b1.clone();調(diào)用的那一刻b1和b2擁有相同的值,在這里,b1.i = b2.i = 11。
要知道不是所有的類都能實(shí)現(xiàn)深度clone的。例如,如果把上面的CloneB類中的UnCloneA類型變量改成StringBuffer類型,看一下JDK API中關(guān)于StringBuffer的說明,StringBuffer沒有重載clone()方法,更為嚴(yán)重的是StringBuffer還是一個(gè)final類,這也是說我們也不能用繼承的辦法間接實(shí)現(xiàn)StringBuffer的clone。如果一個(gè)類中包含有StringBuffer類型對象或和StringBuffer相似類的對象,我們有兩種選擇:要么只能實(shí)現(xiàn)影子clone,要么就在類的clone()方法中加一句(假設(shè)是SringBuffer對象,而且變量名仍是unCA): o.unCA = new StringBuffer(unCA.toString()); //原來的是:o.unCA = (UnCloneA)unCA.clone();
還要知道的是除了基本數(shù)據(jù)類型能自動實(shí)現(xiàn)深度clone以外,String對象是一個(gè)例外,它c(diǎn)lone后的表現(xiàn)好象也實(shí)現(xiàn)了深度clone,雖然這只是一個(gè)假象,但卻大大方便了我們的編程。