1. 程式人生 > >Object物件之clone方法

Object物件之clone方法

克隆的目的:快速建立一個已有物件的副本。

克隆的步驟:

  1. 建立一個物件
  2. 將原有物件的資料匯入到新建立的資料中

1. Object的clone()原始碼簡介

  1. /**
  2. * Creates and returns a copy of this {@code Object}. The default
  3. * implementation returns a so-called "shallow" copy: It creates a new
  4. * instance of the same class and then copies the field values (including
  5. * object references) from this instance to the new instance. A "deep" copy,
  6. * in contrast, would also recursively clone nested objects. A subclass that
  7. * needs to implement this kind of cloning should call {@code super.clone()}
  8. * to create the new instance and then create deep copies of the nested,
  9. * mutable objects.
  10. *
  11. * @return a copy of this object.
  12. * @throws CloneNotSupportedException
  13. * if this object's class does not implement the {@code
  14. * Cloneable} interface.
  15. */
  16. protected Object clone() throws CloneNotSupportedException {
  17. if (!(this instanceof Cloneable)) {
  18. throw new CloneNotSupportedException("Class doesn't implement Cloneable"
    );
  19. }
  20. return internalClone((Cloneable) this);
  21. }
  22. /*
  23. * Native helper method for cloning.
  24. */
  25. private native Object internalClone(Cloneable o);

clone方法首先會判物件是否實現了Cloneable介面,若無則丟擲CloneNotSupportedException, 最後會呼叫internalClone. intervalClone是一個native方法,一般來說native方法的執行效率高於非native方法。

當某個類要複寫clone方法時,要繼承Cloneable介面。通常的克隆物件都是通過super.clone()方法來克隆物件。

2.淺克隆(shadow clone)

   克隆就是複製一個物件的複本.若只需要複製物件的欄位值(對於基本資料型別,如:int,long,float等,則複製值;對於複合資料型別僅複製該欄位值,如陣列變數則複製地址,對於物件變數則複製物件的reference。

例子:

  1. public class ShadowClone implements Cloneable{
  2. private int a; // 基本型別
  3. private int[] b; // 非基本型別
  4. // 重寫Object.clone()方法,並把protected改為public
  5. @Override
  6. public Object clone(){
  7. ShadowClone sc = null;
  8. try
  9. {
  10. sc = (ShadowClone) super.clone();
  11. } catch (CloneNotSupportedException e){
  12. e.printStackTrace();
  13. }
  14. return sc;
  15. }
  16. public int getA()
  17. {
  18. return a;
  19. }
  20. public void setA(int a)
  21. {
  22. this.a = a;
  23. }
  24. public int[] getB() {
  25. return b;
  26. }
  27. public void setB(int[] b) {
  28. this.b = b;
  29. }
  30. }
然後進行測試:
  1. public class Test{
  2. public static void main(String[] args) throws CloneNotSupportedException{
  3. ShadowClone c1 = new ShadowClone();
  4. //對c1賦值
  5. c1.setA(100) ;
  6. c1.setB(new int[]{1000}) ;
  7. System.out.println("克隆前c1: a="+c1.getA()+" b="+c1.getB()[0]);
  8. //克隆出物件c2,並對c2的屬性A,B,C進行修改
  9. ShadowClone c2 = (ShadowClone) c1.clone();
  10. //對c2進行修改
  11. c2.setA(50) ;
  12. int []a = c2.getB() ;
  13. a[0]=5 ;
  14. c2.setB(a);
  15. System.out.println("克隆前c1: a="+c1.getA()+" b="+c1.getB()[0]);
  16. System.out.println("克隆後c2: a="+c2.getA()+ " b[0]="+c2.getB()[0]);
  17. }
  18. }
結果為:

克隆前c1:  a=100 b=1000
克隆前c1:  a=100 b=5
克隆後c2:  a=50 b[0]=5


c1和c2的物件模型:

                                                                     

可以看出,基本型別可以使用淺克隆,而對於引用型別,由於引用的是內容相同,所以改變c2例項物件中的屬性就會影響到c1。所以引用型別需要使用深克隆。另外,在開發一個不可變類的時候,如果這個不可變類中成員有引用型別,則就需要通過深克隆來達到不可變的目的。

3.深克隆(deep clone)

    深克隆與淺克隆的區別在於對複合資料型別的複製。若物件中的某個欄位為複合型別,在克隆物件的時候,需要為該欄位重新建立一個物件。

例子:

  1. public class DeepClone implements Cloneable {
  2. private int a; // 基本型別
  3. private int[] b; // 非基本型別
  4. // 重寫Object.clone()方法,並把protected改為public
  5. @Override
  6. public Object clone(){
  7. DeepClone sc = null;
  8. try
  9. {
  10. sc = (DeepClone) super.clone();
  11. int[] t = sc.getB();
  12. int[] b1 = new int[t.length];
  13. for (int i = 0; i < b1.length; i++) {
  14. b1[i] = t[i];
  15. }
  16. sc.setB(b1);
  17. } catch (CloneNotSupportedException e){
  18. e.printStackTrace();
  19. }
  20. return sc;
  21. }
  22. public int getA()
  23. {
  24. return a;
  25. }
  26. public void setA(int a)
  27. {
  28. this.a = a;
  29. }
  30. public int[] getB() {
  31. return b;
  32. }
  33. public void setB(int[] b) {
  34. this.b = b;
  35. }
  36. }

結果為:

克隆前c1:  a=100 b=1000

克隆前c1:  a=100 b=1000
克隆後c2:  a=50 b[0]=5


物件模型:

                                        

4、總結:

  1. 克隆方法用於建立物件的拷貝,為了使用clone方法,類必須實現java.lang.Cloneable介面重寫protected方法clone,如果沒有實現Clonebale介面會丟擲CloneNotSupportedException.
  2. 在克隆java物件的時候不會呼叫構造器
  3. java提供一種叫淺拷貝(shallow copy)的預設方式實現clone,建立好物件的副本後然後通過賦值拷貝內容,意味著如果你的類包含引用型別,那麼原始物件和克隆都將指向相同的引用內容,這是很危險的,因為發生在可變的欄位上任何改變將反應到他們所引用的共同內容上。為了避免這種情況,需要對引用的內容進行深度克隆。