java中的深浅克隆
假設(shè)有一個(gè)對(duì)象object,在某處又需要一個(gè)跟object一樣的實(shí)例object2,強(qiáng)調(diào)的是object和object2是兩個(gè)獨(dú)立的實(shí)例,只是在開始的時(shí)候,他們是具有相同狀態(tài)的(屬性字段的值都相同)。遇到這種情況的做法一般是,重新new一個(gè)對(duì)象object2,將object的字段值賦予object2,即:object2=object; 這樣的話兩個(gè)引用仍然指向的是同一個(gè)對(duì)象,不是兩個(gè)對(duì)象。
克隆方法clone()
Java中跟克隆有關(guān)的兩個(gè)類分別是Cloneable接口和Object類中的clone方法,通過兩者的協(xié)作來實(shí)現(xiàn)克隆。
首先來看看Object的clone()源代碼:
/*** Creates and returns a copy of this {@code Object}. The default* implementation returns a so-called "shallow" copy: It creates a new* instance of the same class and then copies the field values (including* object references) from this instance to the new instance. A "deep" copy,* in contrast, would also recursively clone nested objects. A subclass that* needs to implement this kind of cloning should call {@code super.clone()}* to create the new instance and then create deep copies of the nested,* mutable objects.** @return a copy of this object.* @throws CloneNotSupportedException* if this object's class does not implement the {@code* Cloneable} interface.*/protected Object clone() throws CloneNotSupportedException {if (!(this instanceof Cloneable)) {throw new CloneNotSupportedException("Class doesn't implement Cloneable");}return internalClone((Cloneable) this);}/** Native helper method for cloning.*/private native Object internalClone(Cloneable o);首先看一下java api doc中關(guān)于Cloneable接口和Object類中的clone方法的描述:
java.lang.Cloneable 接口(以下源引JavaTM 2 Platform Standard Ed. 5.0 API DOC)
此類實(shí)現(xiàn)了 Cloneable 接口,以指示 Object.clone() 方法可以合法地對(duì)該類實(shí)例進(jìn)行按字段復(fù)制。 如果在沒有實(shí)現(xiàn) Cloneable 接口的實(shí)例上調(diào)用 Object 的 clone 方法,則會(huì)導(dǎo)致拋出 CloneNotSupportedException異常。?
按照慣例,實(shí)現(xiàn)此接口的類應(yīng)該使用公共方法重寫 Object.clone(它是受保護(hù)的)。請(qǐng)參閱 Object.clone(),以獲得有關(guān)重寫此方法的詳細(xì)信息。?
注意,此接口不包含 clone 方法。因此,因?yàn)槟硞€(gè)對(duì)象實(shí)現(xiàn)了此接口就克隆它是不可能的。即使 clone 方法是反射性調(diào)用的,也無法保證它將獲得成功。
Cloneable接口沒有任何方法,僅是個(gè)標(biāo)志接口(tagging interface),若要具有克隆能力,實(shí)現(xiàn)Cloneable接口的類必須重寫從Object繼承來的clone方法,并調(diào)用Object的clone方法(見下面Object#clone的定義),重寫后的方法應(yīng)為public 的。
clone方法首先會(huì)判對(duì)象是否實(shí)現(xiàn)了Cloneable接口,若無則拋出CloneNotSupportedException, 最后會(huì)調(diào)用internalClone. intervalClone是一個(gè)native方法,一般來說native方法的執(zhí)行效率高于非native方法。
當(dāng)某個(gè)類要復(fù)寫clone方法時(shí),要繼承Cloneable接口。通常的克隆對(duì)象都是通過super.clone()方法來克隆對(duì)象。
淺克隆(shadow clone)
? ?克隆就是復(fù)制一個(gè)對(duì)象的復(fù)本,若只需要復(fù)制對(duì)象的字段值(對(duì)于基本數(shù)據(jù)類型,如:int,long,float等,則復(fù)制值;對(duì)于復(fù)合數(shù)據(jù)類型僅復(fù)制該字段值,如數(shù)組變量則復(fù)制地址,對(duì)于對(duì)象變量則復(fù)制對(duì)象的reference。
舉個(gè)例子:
public class ShadowClone implements Cloneable{private int a; // 基本類型private int[] b; // 非基本類型// 重寫Object.clone()方法,并把protected改為public @Overridepublic Object clone(){ShadowClone sc = null;try{sc = (ShadowClone) super.clone();} catch (CloneNotSupportedException e){e.printStackTrace();}return sc;}public int getA(){return a;}public void setA(int a){this.a = a;}public int[] getB() {return b;}public void setB(int[] b) {this.b = b;} }測(cè)試代碼如下:
public class Test{public static void main(String[] args) throws CloneNotSupportedException{ShadowClone c1 = new ShadowClone();//對(duì)c1賦值c1.setA(100) ;c1.setB(new int[]{1000}) ;System.out.println("克隆前c1: a="+c1.getA()+" b="+c1.getB()[0]);//克隆出對(duì)象c2,并對(duì)c2的屬性A,B,C進(jìn)行修改ShadowClone c2 = (ShadowClone) c1.clone();//對(duì)c2進(jìn)行修改c2.setA(50) ;int []a = c2.getB() ;a[0]=5 ;c2.setB(a);System.out.println("克隆前c1: a="+c1.getA()+" b="+c1.getB()[0]);System.out.println("克隆后c2: a="+c2.getA()+ " b[0]="+c2.getB()[0]);} }運(yùn)行結(jié)果:
克隆前c1: ?a=100 b=1000
克隆前c1: ?a=100 b=5
克隆后c2: ?a=50 b[0]=5
c1和c2的對(duì)象模型:
可以看出,基本類型可以使用淺克隆,而對(duì)于引用類型,由于引用的是內(nèi)容相同,所以改變c2實(shí)例對(duì)象中的屬性就會(huì)影響到c1。所以引用類型需要使用深克隆。另外,在開發(fā)一個(gè)不可變類的時(shí)候,如果這個(gè)不可變類中成員有引用類型,則就需要通過深克隆來達(dá)到不可變的目的。
深克隆(deep clone)
深克隆與淺克隆的區(qū)別在于對(duì)復(fù)合數(shù)據(jù)類型的復(fù)制。若對(duì)象中的某個(gè)字段為復(fù)合類型,在克隆對(duì)象的時(shí)候,需要為該字段重新創(chuàng)建一個(gè)對(duì)象。
再舉一個(gè)例子:
public class DeepClone implements Cloneable {private int a; // 基本類型private int[] b; // 非基本類型// 重寫Object.clone()方法,并把protected改為public @Overridepublic Object clone(){DeepClone sc = null;try{sc = (DeepClone) super.clone();int[] t = sc.getB();int[] b1 = new int[t.length];for (int i = 0; i < b1.length; i++) {b1[i] = t[i];}sc.setB(b1);} catch (CloneNotSupportedException e){e.printStackTrace();}return sc;}public int getA(){return a;}public void setA(int a){this.a = a;}public int[] getB() {return b;}public void setB(int[] b) {this.b = b;} }運(yùn)行結(jié)果:
克隆前c1: ?a=100 b=1000
克隆前c1: ?a=100 b=1000
克隆后c2: ?a=50 b[0]=5
對(duì)象模型:
使用序列化實(shí)現(xiàn)深克隆
public class DeepClone implements Serializable{private int a;private int[] b;public int getA() {return a;}public void setA(int a){this.a = a;}public int[] getB() {return b;}public void setB(int[] b) {this.b = b;} }然后編寫測(cè)試類:
package test2;import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream;public class Test2{public static void main(String[] args) throws CloneNotSupportedException{Test2 t = new Test2();DeepClone dc1 = new DeepClone();// 對(duì)dc1賦值dc1.setA(100);dc1.setB(new int[] { 1000 });System.out.println("克隆前dc1: a=" + dc1.getA()+"b[0]=" + dc1.getB()[0]);DeepClone dc2 = (DeepClone) t.deepClone(dc1);// 對(duì)c2進(jìn)行修改dc2.setA(50);int[] a = dc2.getB();a[0] = 500;System.out.println("克隆后dc1: a=" + dc1.getA()+"b[0]=" + dc1.getB()[0]);System.out.println("克隆后dc2: a=" + dc2.getA()+"b[0]=" + dc2.getB()[0]);}// 用序列化與反序列化實(shí)現(xiàn)深克隆public Object deepClone(Object src){Object o = null;try{if (src != null){ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos);oos.writeObject(src);oos.close();ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bais);o = ois.readObject();ois.close();}} catch (IOException e){e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}return o;} }運(yùn)行后的結(jié)果如下:
克隆前dc1: a=100 b[0]=1000
克隆后dc1: a=100 b[0]=1000
克隆后dc2: a=50 b[0]=500
可以看到,兩個(gè)引用所指向的對(duì)象在堆中相互獨(dú)立,互不干擾,這樣就實(shí)現(xiàn)了深度克隆。
總結(jié):
1、克隆方法用于創(chuàng)建對(duì)象的拷貝,為了使用clone方法,類必須實(shí)現(xiàn)java.lang.Cloneable接口重寫protected方法clone,如果沒有實(shí)現(xiàn)Clonebale接口會(huì)拋出CloneNotSupportedException.
2、在克隆java對(duì)象的時(shí)候不會(huì)調(diào)用構(gòu)造器
3、java提供一種叫淺拷貝(shallow copy)的默認(rèn)方式實(shí)現(xiàn)clone,創(chuàng)建好對(duì)象的副本后然后通過賦值拷貝內(nèi)容,意味著如果你的類包含引用類型,那么原始對(duì)象和克隆都將指向相同的引用內(nèi)容,這是很危險(xiǎn)的,因?yàn)榘l(fā)生在可變的字段上任何改變將反應(yīng)到他們所引用的共同內(nèi)容上。為了避免這種情況,需要對(duì)引用的內(nèi)容進(jìn)行深度克隆。
4、按照約定,實(shí)例的克隆應(yīng)該通過調(diào)用super.clone()獲取,這樣有助克隆對(duì)象的不變性。如:clone!=original和clone.getClass()==original.getClass(),盡管這些不是必須的
參考資料
http://www.cnblogs.com/shishm/archive/2011/10/10/2205743.html
http://blog.csdn.net/bigconvience/article/details/25025561
總結(jié)
以上是生活随笔為你收集整理的java中的深浅克隆的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java学习笔记16--I/O流和文件
- 下一篇: java设计模式0--设计模式简介