- Java核心技術·卷Ⅱ:高級特性(原書第10版)
- (美)凱S.霍斯特曼
- 872字
- 2020-10-30 18:10:42
2.4.3 修改默認的序列化機制
某些數據域是不可以序列化的,例如,只對本地方法有意義的存儲文件句柄或窗口句柄的整數值,這種信息在稍后重新加載對象或將其傳送到其他機器上時都是沒有用處的。事實上,這種域的值如果不恰當,還會引起本地方法崩潰。Java擁有一種很簡單的機制來防止這種域被序列化,那就是將它們標記成是transient的。如果這些域屬于不可序列化的類,你也需要將它們標記成transient的。瞬時的域在對象被序列化時總是被跳過的。
序列化機制為單個的類提供了一種方式,去向默認的讀寫行為添加驗證或任何其他想要的行為。可序列化的類可以定義具有下列簽名的方法:

之后,數據域就再也不會被自動序列化,取而代之的是調用這些方法。
下面是一個典型的示例。在java.awt.geom包中有大量的類都是不可序列化的,例如Point2D.Double。現在假設你想要序列化一個LabeledPoint類,它存儲了一個String和一個Point2D.Double。首先,你需要將Point2D.Double標記成transient,以避免拋出NotSerializableException。

在writeObject方法中,我們首先通過調用defaultWriteObject方法寫出對象描述符和String域label,這是ObjectOutputStream類中的一個特殊的方法,它只能在可序列化類的writeObject方法中被調用。然后,我們使用標準的DataOutput調用寫出點的坐標。

在readObject方法中,我們反過來執行上述過程:
另一個例子是java.util.Date類,它提供了自己的readObject和writeObject方法,這些方法將日期寫出為從紀元(UTC時間1970年1月1日0點)開始的毫秒數。Date類有一個復雜的內部表示,為了優化查詢,它存儲了一個Calendar對象和一個毫秒計數值。Calendar的狀態是冗余的,因此并不需要保存。
readObject和writeObject方法只需要保存和加載它們的數據域,而不需要關心超類數據和任何其他類的信息。
除了讓序列化機制來保存和恢復對象數據,類還可以定義它自己的機制。為了做到這一點,這個類必須實現Externalizable接口,這需要它定義兩個方法:

與前面一節描述的readObject和writeObject不同,這些方法對包括超類數據在內的整個對象的存儲和恢復負全責。在寫出對象時,序列化機制在輸出流中僅僅只是記錄該對象所屬的類。在讀入可外部化的類時,對象輸入流將用無參構造器創建一個對象,然后調用readExternal方法。下面展示了如何為Employee類實現這些方法:

警告:readObject和writeObject方法是私有的,并且只能被序列化機制調用。與此不同的是,readExternal和writeExternal方法是公共的。特別是,readExternal還潛在地允許修改現有對象的狀態。