java中的clone機制

爲什麼要使用clone?

在實際編程過程中,我們常常要遇到這種情況:有一個對象A,在某一時刻A中已經包含了一些有效值,此時可能會需要一個和A完全相同新對象B,並且此後對B任何改動都不會影響到A中的值,也就是說,A與B是兩個獨立的對象,但B的初始值是由A對象確定的。在這種情況下常常使用clone,但解決上述問題方法很多,如先new一個類,然後把原始對象中的信息賦到新對象中,那爲什麼需要clone呢?

(1)實現clone方法簡單、方便

(2)Object類的clone()一個native方法,native方法的效率一般來說都是遠高於java中的非native方法,因此clone方法是高效的

影子clone與深度clone

由於java中 ,對非基本類型變量,它們保存的僅僅是對象的引用,所以使用上要特別小心。請看以下一組代碼

public class Sheep implements Cloneable{
    
private String name;
    
private int number;
    
public void setName(String arg) {
        name 
= arg;
    }

    
public String getName() {
        
return name;
    }

    
public Object clone() throws CloneNotSupportedException {
        
return super.clone();
    }

}


public class Sheepfold implements Cloneable {
    
public Sheep sheep;
    
public String name;
    
public int number;
    
public Sheepfold() {
        sheep 
= new Sheep();
    }

    
public Object clone() throws CloneNotSupportedException {
        
return super.clone();
    }

}


public class Main {
  
public static void main(String[] args) throws Exception {
      Sheepfold fold 
= new Sheepfold();
      fold.name 
= "小羊圈";
      fold.number
=10;
           fold.sheep.setName(
"小羊");
           Sheepfold fold2 
= (Sheepfold)fold.clone();
           System.out.println(
"  fold2.name = " + fold2.name);
           System.out.println(
"  fold2.number = " + fold2.number);
           System.out.println(
"  fold2.sheep.getName() = " + fold2.sheep.getName());
           fold2.name 
= "大羊圈";
           fold2.sheep.setName(
"大羊");
           fold2.number
=100;
           System.out.println(
"=====================================");
           System.out.println(
"  fold2.name = " + fold2.name);
           System.out.println(
"  fold2.number = " + fold2.number);
           System.out.println(
"* fold2.sheep.getName() = " + fold2.sheep.getName());
           System.out.println(
"  fold.name = " + fold.name);
           System.out.println(
"  fold.number = " + fold.number);
           System.out.println(
"* fold.sheep.getName() = " + fold.sheep.getName());
           System.out.println(
"=====================================");
    }

}

輸出結果

 fold2.name = 小羊圈
 flod2.number = 10
  fold2.sheep.getName() = 小羊
=====================================
  fold2.name = 大羊圈
  flod2.number = 100
* fold2.sheep.getName() = 大羊
  fold.name = 小羊圈
  flod2.number = 10
* fold.sheep.getName() = 大羊
=====================================

在此之前,我們只對fold2.sheep的name賦過值。爲什麼fold.sheep的name也變爲了“大羊”呢?原因很簡單,因爲它們是指向同一個對象的不同引用。從中可以看出,調用Object類中clone()方法時,首先在內存中劃分一塊同原對象相同的空間,然後將原對象的內容原樣拷貝至新對象。我們知道,java中有基本數據類型,對於基本數據類型,這樣的操作是沒有問題的,但對非基本類型變量,它們保存的僅僅是對象的引用,這也是爲什麼clone後非基本類型變量和原對象中的變量指向同一個對象的原因。可能你已經注意到,程序中用到了String類型,即對象,爲什麼沒有出現引用指向同一地址的情況?這是因爲String是一個不可更改的類(immutable class),每次給它賦值時,都會產生一個新的String對象。如String str = "a"; str += "b";在這兩句代碼中,當執行str += "b"時,實際上是重新成生了一個值爲“ab”的String對象,即重新分配了一塊內存空間。以上clone方法通常被稱爲“影子clone”。“影子clone”給我們留下了一個問題,即多個引用指向同一個對象。如何解決該問題呢?答案爲“深度clone”。把上面的例子改成深度clone很簡單,只需將Sheepfold的clone()方法改爲如下即可:
    public Object clone() throws CloneNotSupportedException {
        Sheepfold fold = (Sheepfold)super.clone();
        sheep = (Sheep)fold.sheep.clone();
        return fold;
    }

發佈了31 篇原創文章 · 獲贊 5 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章