2011-08-16 34 views
60

最好不要公開對象(實體)的內部引用。所以如果一個對象有一個java.util.Date類型的字段,那麼例如這個字段的getter不應該返回原始日期,而是返回它的一個副本。java.util.Date克隆或複製到不公開內部引用

但是對於java.util.Date有創建一個副本兩種常用方法:

  • 克隆:(Date) originalDate.clone()
  • 通過拷貝構造函數new Date(originalDate.getTime())

我的問題是,這方式更好,爲什麼?

回答

40

如果它絕對只是一個Date,它不會有任何區別。

如果實際的對象可能是一個子類的Date(如java.sql.Date那麼我希望clone()將保留額外信息(包括它是類),而調用構造函數不會。

順便說一句,如果你使用Joda Time你不會有這個問題,因爲有很多不可變的類型可供使用。這也是一個更好的API :)

+6

從Java 8開始,可以使用java.time API來代替Joda Time。 – Akash

33

閱讀Effective Java。創建副本的首選方法是使用複製構造函數方法。

Bill Venners:在你的書中,你推薦使用拷貝構造函數 而不是實現Cloneable和寫克隆。你可以 詳細說明嗎? Josh Bloch:如果您在我的書中閱讀過有關克隆的項目,特別是如果您在各行之間閱讀,您會知道我認爲 克隆已經被深深打破。有一些設計缺陷,最大的是 ,即Cloneable接口沒有克隆方法。 而這意味着它根本行不通:做一些事情Cloneable 沒有說你可以用它做什麼。相反,它說 內部可以做些什麼。它說如果通過反覆調用 super.clone它最終會調用Object的克隆方法,這個 方法將返回原始字段副本。

+5

這是在你自己的類中設計克隆的首選方式,儘管它要求調用者要麼*知道*使用了哪個具體的子類* care *要使用哪個具體的子類。查看我的答案,瞭解它是如何產生影響的一個例子。 –

14

如果您是防禦性編碼,您需要複製構造函數。請參閱this passage from Effective Java

請注意,我們沒有使用Date的克隆方法來製作防禦副本。因爲Date是非終結的,所以克隆方法不保證返回一個類爲java.util.Date的對象;它可能會返回專門爲惡意惡作劇設計的不可信子類的實例。例如,這樣的子類可以在其創建時記錄對私有靜態列表中每個實例的引用,並允許攻擊者訪問該列表。這會讓攻擊者自由掌控所有實例。爲了防止這種攻擊,請不要使用克隆方法爲其類型可由不受信任方進行子類化的參數製作防禦副本。