天天看點

《Java核心技術 卷Ⅱ 進階特性(原書第10版)》一2.4.4 序列化單例和類型安全的枚舉

在序列化和反序列化時,如果目标對象是唯一的,那麼你必須加倍當心,這通常會在實作單例和類型安全的枚舉時發生。

如果你使用Java語言的enum結構,那麼你就不必擔心序列化,它能夠正常工作。但是,假設你在維護遺留代碼,其中包含下面這樣的枚舉類型:

《Java核心技術 卷Ⅱ 進階特性(原書第10版)》一2.4.4 序列化單例和類型安全的枚舉
《Java核心技術 卷Ⅱ 進階特性(原書第10版)》一2.4.4 序列化單例和類型安全的枚舉

這種風格在枚舉被添加到Java語言中之前是很普遍的。注意,其構造器是私有的。是以,不可能建立出超出Orientation.HORIZONTAL和Orientation.VERTICAL之外的對象。特别是,你可以使用==操作符來測試對象的等同性:

當類型安全的枚舉實作Serializable接口時,你必須牢記存在着一種重要的變化,此時,預設的序列化機制是不适用的。假設我們寫出一個Orientation類型的值,并再次将其讀回:

《Java核心技術 卷Ⅱ 進階特性(原書第10版)》一2.4.4 序列化單例和類型安全的枚舉

将失敗。事實上,saved的值是Orientation類型的一個全新的對象,它與任何預定義的常量都不等同。即使構造器是私有的,序列化機制也可以建立新的對象!

為了解決這個問題,你需要定義另外一種稱為readResolve的特殊序列化方法。如果定義了readResolve方法,在對象被序列化之後就會調用它。它必須傳回一個對象,而該對象之後會成為readObject的傳回值。在上面的情況中,readResolve方法将檢查value域并傳回恰當的枚舉常量:

《Java核心技術 卷Ⅱ 進階特性(原書第10版)》一2.4.4 序列化單例和類型安全的枚舉

請記住向遺留代碼中所有類型安全的枚舉以及向所有支援單例設計模式的類中添加readResolve方法。