java clone問題

在java中,如果需要有拷貝問題,都會使用到父類ObjectClone方法,能夠為我們提供對象的拷貝方法,在使用過程中這里會有很多各式各樣的問題。這里簡單羅列一下個人所遇到的一些問題:

1. 實現 Cloneable接口以及為何使用super.clone()

Object的clone方法,就是拷貝一份副本出來,要求副本狀態或者屬性與原型要一致,但是相互要獨立,改變原型或者副本,兩者不會相互干擾。

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

  • 在沒有實現 Cloneable 接口的實例上調用Objectclone方法,則會導致拋出 CloneNotSupportedException異常。

在子類覆蓋了clone方法后,調用super.clone()方法,Object的clone()是一個native方法,會返回一個拷貝的實例。

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

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

使用super.clone()的好處是會檢查是否實現了Cloneable,否則會拋異常,換句話說就是 ** 防止不可復制的類調用clone()方法**

另外一種方法是提供拷貝構造器實現對象拷貝,好處就是構造器可以傳遞參數,而clone()卻不可以。

2. 深拷貝和淺拷貝問題

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

淺拷貝、深拷貝圖:

淺拷貝
深拷貝

** super.clone()默認是只進行淺拷貝的**

  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();//事實上,未覆蓋的clone()方法調用的是Object的clone(),是淺復制
   
  //測試代碼
  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會生成一個新的對象
  print(Bom.parent == Jason.parent); // "true", 指向相同的對象

除了不可變對象或者基本數據類型,java的對象復制只是對引用的復制,所以拷貝的副本和原型引用了同一個對象,因此根據業務需求,有時候我們需要深度拷貝:

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

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就是另外一種構造器,你必須保證他不能傷害到原始的對象,并確保正確地創建被克隆。

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

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

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

  3. 通過迭代器循環逐個進行深度的clone()

Clone方法就像構造方法,所以在里面最好只調用復制對象的final方法或者private方法,否則子類有可能重寫該方法,這樣子類在調用clone時會優先調用重寫的方法,可能復制出來的對象不一致了。

總結

最后的原則:

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

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

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

參考

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,908評論 6 541
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,324評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,018評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,675評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,417評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,783評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,779評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,960評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,522評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,267評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,471評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,009評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,698評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,099評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,386評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,204評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,436評論 2 378

推薦閱讀更多精彩內容

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