爲什麼要使用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中 ,對非基本類型變量,它們保存的僅僅是對象的引用,所以使用上要特別小心。請看以下一組代碼
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;
}