假設有一個對象object,在某處又需要一個跟object一樣的實例object2,強調的是object和object2是兩個獨立的實例,只是在開始的時候,他們是具有相同狀態的(屬性字段的值都相同)。遇到這種情況的做法一般是,重新new一個對象object2,將object的字段值賦予object2,即:object2=object; 這樣的話兩個引用仍然指向的是同一個對象,不是兩個對象。
克隆方法clone()Java中跟克隆有關的兩個類分別是Cloneable接口和Object類中的clone方法,通過兩者的協作來實現克隆。
首先來看看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中關于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()方法來克隆對象。
淺克隆(shadow clone)克隆就是復制一個對象的復本,若只需要復制對象的字段值(對于基本數據類型,如:int,long,float等,則復制值;對于復合數據類型僅復制該字段值,如數組變量則復制地址,對于對象變量則復制對象的reference。
舉個例子:
public class ShadowClone implements Cloneable{ private int a; // 基本類型 private int[] b; // 非基本類型 // 重寫Object.clone()方法,并把protected改為public @Override public 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; } }
測試代碼如下:
public class Test{ public static void main(String[] args) throws CloneNotSupportedException{ ShadowClone c1 = new ShadowClone(); //對c1賦值 c1.setA(100) ; c1.setB(new int[]{1000}) ; System.out.println("克隆前c1: a="+c1.getA()+" b="+c1.getB()[0]); //克隆出對象c2,并對c2的屬性A,B,C進行修改 ShadowClone c2 = (ShadowClone) c1.clone(); //對c2進行修改 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]); }}
運行結果:
克隆前c1: a=100 b=1000克隆前c1: a=100 b=5克隆后c2: a=50 b[0]=5
c1和c2的對象模型:
可以看出,基本類型可以使用淺克隆,而對于引用類型,由于引用的是內容相同,所以改變c2實例對象中的屬性就會影響到c1。所以引用類型需要使用深克隆。另外,在開發一個不可變類的時候,如果這個不可變類中成員有引用類型,則就需要通過深克隆來達到不可變的目的。
深克隆(deep clone)深克隆與淺克隆的區別在于對復合數據類型的復制。若對象中的某個字段為復合類型,在克隆對象的時候,需要為該字段重新創建一個對象。
再舉一個例子:
public class DeepClone implements Cloneable { private int a; // 基本類型 private int[] b; // 非基本類型 // 重寫Object.clone()方法,并把protected改為public @Override public 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; }}
運行結果:
克隆前c1: a=100 b=1000
克隆前c1: a=100 b=1000
克隆后c2: a=50 b[0]=5
對象模型:
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; } }
然后編寫測試類:
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(); // 對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); // 對c2進行修改 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]); } // 用序列化與反序列化實現深克隆 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; }}
運行后的結果如下:
克隆前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(),盡管這些不是必須的
參考資料http://www.49028c.com/shishm/archive/2011/10/10/2205743.html
http://blog.csdn.net/bigconvience/article/details/25025561
新聞熱點
疑難解答