java clone問題

在java中,如果需要有拷貝問題,都會(huì)使用到父類ObjectClone方法,能夠?yàn)槲覀兲峁?duì)象的拷貝方法,在使用過程中這里會(huì)有很多各式各樣的問題。這里簡(jiǎn)單羅列一下個(gè)人所遇到的一些問題:

1. 實(shí)現(xiàn) Cloneable接口以及為何使用super.clone()

Object的clone方法,就是拷貝一份副本出來,要求副本狀態(tài)或者屬性與原型要一致,但是相互要獨(dú)立,改變?cè)突蛘吒北?,兩者不?huì)相互干擾。

 Object a = new Object();
 Object b = a;
//雖然a和b相同,但是二者是指向同一個(gè)對(duì)象,所以不是克隆出來的
  • Object提供了clone方法,但是需要子類實(shí)現(xiàn)Cloneable接口,換句話說就是,只有實(shí)現(xiàn)Cloneable接口的類能被拷貝。

  • 在沒有實(shí)現(xiàn) Cloneable 接口的實(shí)例上調(diào)用Objectclone方法,則會(huì)導(dǎo)致拋出 CloneNotSupportedException異常。

在子類覆蓋了clone方法后,調(diào)用super.clone()方法,Object的clone()是一個(gè)native方法,會(huì)返回一個(gè)拷貝的實(shí)例。

@Override
public Person clone(){
      try{
            Person copy = (Person) super.clone();//返回一個(gè)拷貝的實(shí)例
      }catch(CloneNotSupportedException e){
            e.printStackTrace();
      }
}

值得注意的是,super.clone()只是進(jìn)行淺拷貝,這一點(diǎn)會(huì)在下面進(jìn)行討論。

使用super.clone()的好處是會(huì)檢查是否實(shí)現(xiàn)了Cloneable,否則會(huì)拋異常,換句話說就是 ** 防止不可復(fù)制的類調(diào)用clone()方法**

另外一種方法是提供拷貝構(gòu)造器實(shí)現(xiàn)對(duì)象拷貝,好處就是構(gòu)造器可以傳遞參數(shù),而clone()卻不可以。

2. 深拷貝和淺拷貝問題

淺拷貝是指拷貝對(duì)象時(shí)僅僅拷貝對(duì)象本身(包括對(duì)象中的基本變量),而不拷貝對(duì)象包含的引用指向的對(duì)象。深拷貝不僅拷貝對(duì)象本身,而且拷貝對(duì)象包含的引用指向的所有對(duì)象

淺拷貝、深拷貝圖:

淺拷貝
深拷貝

** super.clone()默認(rèn)是只進(jìn)行淺拷貝的**

  class Person implements Cloneable{
          String name;
          Person parent;
          public Person( String name){
              this.name =  name;
              this.parent = null;
          }
  }

  Person father = new Person("father");  
  Person Jason = new Person("jason");
  Jason.parent = father;
  Person Bom = Jason.clone();//事實(shí)上,未覆蓋的clone()方法調(diào)用的是Object的clone(),是淺復(fù)制
   
  //測(cè)試代碼
  Bom.name = "Bom";
  Bom.parent.name = "mother";
  
  print(Bom.name);    // "Bom"
  print(Bom.parent.name);   // "mother"
  pirnt(Jason.name);   // " Jason"
  pirnt(Jason.parent.name);  // "mother" ,而不是"father"
  print(Bom == Jason); // "false",clone會(huì)生成一個(gè)新的對(duì)象
  print(Bom.parent == Jason.parent); // "true", 指向相同的對(duì)象

除了不可變對(duì)象或者基本數(shù)據(jù)類型,java的對(duì)象復(fù)制只是對(duì)引用的復(fù)制,所以拷貝的副本和原型引用了同一個(gè)對(duì)象,因此根據(jù)業(yè)務(wù)需求,有時(shí)候我們需要深度拷貝:

** 在clone()中,可變的對(duì)象都需要進(jìn)行重寫clone(),保證不會(huì)引用到同一個(gè)對(duì)象,否則會(huì)造成各種問題。**

class Person implements Cloneable{
      String name;
      Person parent;
      public Person( String name){
          this.name =  name;
          this.parent = null;
      }

      public Person clone(){
          Person copy = null;
          try{
               copy = (Person) super.clone();
               if(this.parent != null) 
                  copy.parent = this.parent.clone();
          } catch (CloneNotSupportedException e) {
                e.printStackTrace();
          }
          return copy;
      }
  }

3. 在Iterator中注意的問題

在Effective Java書中寫道,clone就是另外一種構(gòu)造器,你必須保證他不能傷害到原始的對(duì)象,并確保正確地創(chuàng)建被克隆。

在java 容器中,拷貝的方法有:

  1. 直接通過構(gòu)造器初始化,但是只是淺拷貝,拷貝的只是對(duì)象的引用
    List cloneList=new ArrayList(src);

  2. Collections.copy(List desc,List src) 方法,注意desc和src的size,不然會(huì)出現(xiàn)IndexOutOfBoundsException異常,這個(gè)方法同樣是淺拷貝

  3. 通過迭代器循環(huán)逐個(gè)進(jìn)行深度的clone()

Clone方法就像構(gòu)造方法,所以在里面最好只調(diào)用復(fù)制對(duì)象的final方法或者private方法,否則子類有可能重寫該方法,這樣子類在調(diào)用clone時(shí)會(huì)優(yōu)先調(diào)用重寫的方法,可能復(fù)制出來的對(duì)象不一致了。

總結(jié)

最后的原則:

  1. 首先調(diào)用super.clone(),然后修正需要改變的域。

  2. 要么寫一個(gè)良好的clone()方法,否則就改用使用其他方法進(jìn)行拷貝,因?yàn)閏lone會(huì)出現(xiàn)特別多意想之外的問題(大部分是深度和淺度復(fù)制問題),另外一種方法是直接不使用clone方法,而是采用拷貝構(gòu)造器直接生成,自己修正需要改變的域,或者通過序列化的方法進(jìn)行拷貝。

  3. 在clone方法中,一般通過super.clone()獲取拷貝實(shí)例,再修改域,因此不能含有final變量,這是clone的另一個(gè)不足。

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 11.Override clone judiciously 大意為 明智地重寫clone方法 說到clone方法,...
    Mezereon閱讀 815評(píng)論 0 3
  • 對(duì)象的創(chuàng)建與銷毀 Item 1: 使用static工廠方法,而不是構(gòu)造函數(shù)創(chuàng)建對(duì)象:僅僅是創(chuàng)建對(duì)象的方法,并非Fa...
    孫小磊閱讀 2,031評(píng)論 0 3
  • 第一章:Java程序設(shè)計(jì)概述 Java和C++最大的不同在于Java采用的指針模型可以消除重寫內(nèi)存和損壞數(shù)據(jù)的可能...
    loneyzhou閱讀 1,286評(píng)論 1 7
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,767評(píng)論 18 399
  • java筆記第一天 == 和 equals ==比較的比較的是兩個(gè)變量的值是否相等,對(duì)于引用型變量表示的是兩個(gè)變量...
    jmychou閱讀 1,524評(píng)論 0 3