Java進階系列之對象克隆


大家都知道在Java中所有的類都是缺省的繼承自Java語言包中的Object類的,查看它的源碼,你可以把你的JDK目錄下的src.zip復制到其他地方然后解壓,里面就是所有的源碼。發(fā)現(xiàn)里面有一個訪問限定符為protected的方法clone():

protected native Object clone() throws CloneNotSupportedException;

仔細一看,它還是一個native方法,大家都知道native方法是非Java語言實現(xiàn)的代碼,供Java程序調(diào)用的,因為Java程序是運行在JVM虛擬機上面的,要想訪問到比較底層的與操作系統(tǒng)相關(guān)的就沒辦法了,只能由靠近操作系統(tǒng)的語言來實現(xiàn)。

為什么要克???

大家先思考一個問題,為什么需要克隆對象?直接new一個對象不行嗎?答案是:克隆的對象可能包含一些已經(jīng)修改過的屬性,而new出來的對象的屬性都還是初始化時候的值,所以當需要一個新的對象來保存當前對象的“狀態(tài)”就靠clone方法了。那么我把這個對象的臨時屬性一個一個的賦值給我新new的對象不也行嘛?可以是可以,但是一來麻煩不說,二來,大家通過上面的源碼都發(fā)現(xiàn)了clone是一個native方法,就是快啊,在底層實現(xiàn)的。提個醒,我們常見的Object a=new Object();Object b;b=a;這種形式的代碼復制的是引用,即對象在內(nèi)存中的地址,a和b對象仍然指向了同一個對象。而通過clone方法賦值的對象跟原來的對象時同時獨立存在的。扯了這么多廢話,終于進入正題了ORZ。

如何實現(xiàn)克隆

簡單到你不敢相信。直接在你的類的后面聲明implements Cloneable。關(guān)于這個接口,它的源碼如下:

public interface Cloneable {
}

可以看到它是一個空的接口,它的作用就是做標記。如果沒有實現(xiàn)Cloneable接口就直接使用clone方法,程序會拋出CloneNotSupportedException異常。
然后是重寫clone方法,并修改成public訪問級別。舉個經(jīng)典的栗子:

class Outer implements Cloneable {
    public int name;
    public Inner inner;
      
    @Override
    public Object clone() throws CloneNotSupportedException {
      return super.clone();
    }
public static void main(String[] args){
    Outer o_one = new Outer();
    o_one.inner = new Inner("zhangsan");
    try {
    Object obj = o_one.clone();
    Outer o_two = (Outer)obj;
    System.out.println(o_one==o_two);    //打印false
    System.out.println(o_one.inner.name.equals(o_two.inner.name)); //打印true
    } catch (CloneNotSupportedException e) {
     e.printStackTrace();
    }
}
}

上面的代碼實現(xiàn)的其實是淺克隆,淺克隆對于引用類型僅拷貝引用,沒有真正地讓兩個對象獨立開來互相之間沒有任何關(guān)系。由于是淺克隆,使得imp2修改了child的某個屬性后會是的imp1中child的屬性也跟著改變?;蛘弑容^兩個對象的地址:imp1.child==imp2.child,返回的結(jié)果是true。但是,如果一個對象只包含原始數(shù)據(jù)域或者不可變對象域(比如String類型),推薦使用淺克隆。

深克隆

類中的所有引用類型做一些修改,讓它也實現(xiàn)Cloneable接口。然后修改本類中的clone方法:

public class Inner implements  Cloneable{

  public String name;

  public Child(String name) {
      this.name = name;
  }

  @Override
  public String toString() {
      return "Inner的name值為:" + name;
  }

  @Override
  protected Object clone() throws CloneNotSupportedException {
      return super.clone();
  }
}

修改本類的clone方法:

static class Outer implements Cloneable {
  public int count;
  public Inner inner;
      
      
  @Override
  public Object clone() throws CloneNotSupportedException {
      Outer obj = (Outer)super.clone();
      obj.inner = (Inner) inner.clone();
      return obj;
  }
}

畫重點了:

  • 需要重寫clone方法,不僅僅只調(diào)用父類的方法,還需調(diào)用屬性的clone方法;
  • 對象之間100%數(shù)據(jù)分離
  • 如果是對象存在引用類型的屬性,建議使用深克隆
  • 深克隆比淺克隆要更加耗時,效率更低

解決多層克隆問題

如果引用類型里面還包含很多引用類型,或者內(nèi)層引用類型的類里面又包含引用類型,使用clone方法就會很麻煩。這時我們可以用序列化的方式來實現(xiàn)對象的深克隆。

public class Outer implements Serializable{
  private static final long serialVersionUID = 369285298572941L;  //最好是顯式聲明ID
  public Inner inner;
  
  public Outer myclone() {
      Outer outer = null;
      try {
          ByteArrayOutputStream baos = new ByteArrayOutputStream();
          ObjectOutputStream oos = new ObjectOutputStream(baos);
          oos.writeObject(this);
  
          ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
          ObjectInputStream ois = new ObjectInputStream(bais);
          outer = (Outer) ois.readObject();
      } catch (IOException e) {
          e.printStackTrace();
      } catch (ClassNotFoundException e) {
          e.printStackTrace();
      }
      return outer;
  }
}

Inner也必須實現(xiàn)Serializable,否則無法序列化:

public class Inner implements Serializable{
  private static final long serialVersionUID = 872390113109L; //最好是顯式聲明ID
  public String name = "";

  public Inner(String name) {
      this.name = name;
  }

  @Override
  public String toString() {
      return "Inner的name值為:" + name;
  }
}

這樣也能使兩個對象在內(nèi)存空間內(nèi)完全獨立存在,互不影響對方的值。

Tips

  • 在克隆方法中,如果我們需要對可變對象的final域也進行拷貝,由于final的限制,所以實際上是無法編譯通過的。因此為了實現(xiàn)克隆,我們需要考慮舍去該可變對象域的final關(guān)鍵字。
  • 如果你決定用線程安全的類實現(xiàn)Cloneable接口,需要保證它的clone方法做好同步工作。默認的Object.clone方法是沒有做同步的。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容