2010-11-02 24 views
78

我正在尋找一些教程來解釋有關Java Cloneable,但沒有得到任何好的鏈接,堆棧溢出越來越明顯的選擇。關於Java可複製

我想了解以下內容:

  1. Cloneable意味着我們可以有一個克隆或對象的副本,通過 實現Cloneable接口。這樣做的缺點是什麼?
  2. 如果對象是 組合對象,遞歸克隆是如何發生的?

謝謝。

+2

相比什麼優點和缺點? – Galactus 2010-11-02 20:37:52

+0

我讀到,這意味着類是克隆與不克隆的優勢。不知道如何解釋:S – allyourcode 2013-05-25 01:50:28

回答

129

您應該知道的關於Cloneable的第一件事是 - 不要使用它。

這是很難實現與Cloneable克隆權利,並且努力是不值得的。

而不是使用其他選項,如apache-commons SerializationUtils(深度克隆)或BeanUtils(淺 - 克隆),或者只是使用複製構造函數。

See here喬希布洛赫關於克隆與Cloneable,這解釋了該方法的許多缺點的意見。 (Joshua Bloch是Sun的一名員工,並領導開發了許多Java功能。)

+0

我鏈接了Bloch的單詞(而不是引用它們) – Bozho 2010-11-02 20:44:54

+2

請注意Block表示不使用Cloneable。他並不是說不使用克隆(或者至少我不希望)。有很多方法可以簡單地實現克隆,這比使用反射的SerializationUtils或BeanUtils類更高效。看到我的帖子下面的例子。 – Charles 2014-02-15 22:15:53

10
  1. 克隆調用構造對象的額外語言學方式 - 不帶構造函數。
  2. 克隆需要您用CloneNotSupportedException以某種方式對待 - 或者爲了處理它而煩擾客戶端代碼。
  3. 好處很小 - 您只需不必手動編寫複製構造函數即可。

所以,明智地使用Cloneable。與您爲了做正確事情而需要申請的努力相比,它並沒有給您帶來足夠的好處。

+0

正如Bozho所說,不要使用Cloneable。相反,使用複製構造函數。 http://www.javapractices.com/topic/TopicAction.do?Id=12 – Bane 2010-11-02 20:47:54

+0

@Bane,如果你不知道要克隆的對象的類型,如何知道要調用哪個類的拷貝構造函數? – 2010-11-02 22:55:32

+0

@Steve:我不關注。如果你要克隆一個對象,我認爲你已經知道它是什麼類型 - 畢竟,你有計劃克隆的對象。如果有一種情況,你的對象已經失去了它的具體類型,更通用的,你不能使用一個簡單的'實例'來評估它? – Bane 2010-11-03 13:47:45

4

A)複製構造函數克隆沒有很多優點。可能最大的一個是創建完全相同動態類型的新對象(假設聲明的類型是可克隆的並具有公共克隆方法)。 B)默認克隆創建一個淺拷貝,並且它將保持一個淺拷貝,除非你的克隆實現改變了它。這可能是困難的,特別是如果你的班級有最後的字段

Bozho是正確的,克隆可能很難得到正確的。複製構造函數/工廠將滿足大多數需求。

34

Cloneable本身不幸只是一個標記接口,即:它沒有定義clone()方法。

什麼是確定是否會改變受保護的Object.clone()方法的行爲,該方法將爲未實現Cloneable的類拋出CloneNotSupportedException,併爲類所在的類執行成員級淺表副本。

即使這是您正在尋找的行爲,您仍然需要實現自己的clone()方法以公開它。

在實現自己的clone()時,想法是先從super.clone()創建對象,並確保它是正確的類,然後在出現淺度時添加任何字段複製不是你想要的。從clone()調用構造函數會有問題,因爲如果子類想要添加自己的額外的可複製邏輯,這會破壞繼承;如果要調用super.clone(),則在這種情況下會得到錯誤類的對象。

該方法繞過了可能在您的構造函數中定義的任何邏輯,這可能會造成問題。

另一個問題是,任何忘記覆蓋clone()的子類都會自動繼承默認的淺拷貝,這可能不是你想要的在可變狀態的情況下(它現在將在源和拷貝之間共享) 。

大多數開發人員不會因爲這些原因而使用Cloneable,而只是簡單地實現一個複製構造函數。

欲瞭解更多信息和Cloneable的潛在的陷阱,我強烈建議書有效的Java由約書亞布洛赫

5

克隆是一個基本的編程範式。 Java可能以很多方式實施它的事實根本不會減少克隆的需要。而且,很容易實現可以工作的克隆,但是您希望它能夠工作,淺層,深層,混合等等。你甚至可以使用名稱克隆的功能,而不是實現克隆,如果你喜歡。

假設我有類A,B和C,其中B和C是從A派生如果我有這樣一個類型的對象的列表:

ArrayList<A> list1; 

現在,該列表可以包含類型A,B或C的對象。您不知道對象是什麼類型。所以,你不能複製名單如下:

ArrayList<A> list2 = new ArrayList<A>(); 
for(A a : list1) { 
    list2.add(new A(a)); 
} 

如果對象實際上是B或C類的,你就不會得到正確的副本。而且,如果A是抽象的呢?現在,有人提出這樣的建議:

ArrayList<A> list2 = new ArrayList<A>(); 
for(A a : list1) { 
    if(a instanceof A) { 
     list2.add(new A(a)); 
    } else if(a instanceof B) { 
     list2.add(new B(a)); 
    } else if(a instanceof C) { 
     list2.add(new C(a)); 
    } 
} 

這是一個非常非常糟糕的主意。如果你添加一個新的派生類型呢?如果B或C在另一個包裝中,並且您無法在此課程中使用它們,該怎麼辦?

什麼你想要做的是這樣的:

ArrayList<A> list2 = new ArrayList<A>(); 
for(A a : list1) { 
    list2.add(a.clone()); 
} 

很多人都表示,爲什麼克隆的基本Java實現是有問題的。但是,它很容易克服這樣:

在A類:

public A clone() { 
    return new A(this); 
} 

在B類:

@Override 
public B clone() { 
    return new B(this); 
} 

在C類:

@Override 
public C clone() { 
    return new C(this): 
} 

我不執行可複製的,只是使用相同的函數名稱。如果你不喜歡這個,就把它命名爲別的。

+0

我剛剛在回覆您的評論後,在單獨的答案中看到了這一點;我明白了你現在要去的地方,但是有兩件事:1)OP特別詢問使用Cloneable(而不是關於克隆的一般概念),2)你在這裏分開頭髮,試圖區分複製構造函數和通用的克隆概念。你在這裏傳達的想法是有效的,但在它的根源你只是使用一個複製構造函數。 ;) – Bane 2014-02-27 22:53:21

+0

儘管我確實想說我同意你的方法,那就是包含一個A#copyMethod(),而不是強迫用戶直接調用複製構造函數。 – Bane 2014-02-27 22:55:49

0

Cloneable的缺點是什麼?

克隆是非常危險的,如果你是誰抄襲對象有composition.You需要考慮下面這種情況下可能的副作用,因爲克隆創建淺拷貝:

讓說你有一個對象來處理數據庫相關的操作。假設,該對象具有Connection對象作爲屬性之一。

因此,當有人創建了originalObject的克隆,正在創建的對象,比如說cloneObject。 這裏的originalObjectcloneObject對於Connection對象保持相同的參考。

咱們說originalObject關閉Connection對象,所以現在cloneObject將無法​​工作,因爲connection對象是他們之間共享,這是actaually由originalObject關閉。

如果假設您想克隆具有IOStream作爲屬性的對象,則可能會出現類似問題。

如果對象是複合對象,遞歸克隆是如何發生的?

可克隆執行淺拷貝。意思是原始對象和克隆對象的數據將指向相同的參考/內存。 相反,在深拷貝的情況下,原始對象的內存數據被複制到克隆對象的內存中。