天天看點

《Java核心技術 卷Ⅱ 進階特性(原書第10版)》一2.4.3 修改預設的序列化機制

某些資料域是不可以序列化的,例如,隻對本地方法有意義的存儲檔案句柄或視窗句柄的整數值,這種資訊在稍後重新加載對象或将其傳送到其他機器上時都是沒有用處的。事實上,這種域的值如果不恰當,還會引起本地方法崩潰。Java擁有一種很簡單的機制來防止這種域被序列化,那就是将它們标記成是transient的。如果這些域屬于不可序列化的類,你也需要将它們标記成transient的。瞬時的域在對象被序列化時總是被跳過的。

序列化機制為單個的類提供了一種方式,去向預設的讀寫行為添加驗證或任何其他想要的行為。可序列化的類可以定義具有下列簽名的方法:

《Java核心技術 卷Ⅱ 進階特性(原書第10版)》一2.4.3 修改預設的序列化機制

之後,資料域就再也不會被自動序列化,取而代之的是調用這些方法。

下面是一個典型的示例。在java.awt.geom包中有大量的類都是不可序列化的,例如Point2D.Double。現在假設你想要序列化一個LabeledPoint類,它存儲了一個String和一個Point2D.Double。首先,你需要将Point2D.Double标記成transient,以避免抛出NotSerializableException。

《Java核心技術 卷Ⅱ 進階特性(原書第10版)》一2.4.3 修改預設的序列化機制

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

《Java核心技術 卷Ⅱ 進階特性(原書第10版)》一2.4.3 修改預設的序列化機制

另一個例子是java.util.Date類,它提供了自己的readObject和writeObject方法,這些方法将日期寫出為從紀元(UTC時間1970年1月1日0點)開始的毫秒數。Date類有一個複雜的内部表示,為了優化查詢,它存儲了一個Calendar對象和一個毫秒計數值。Calendar的狀态是備援的,是以并不需要儲存。

readObject和writeObject方法隻需要儲存和加載它們的資料域,而不需要關心超類資料和任何其他類的資訊。

除了讓序列化機制來儲存和恢複對象資料,類還可以定義它自己的機制。為了做到這一點,這個類必須實作Externalizable接口,這需要它定義兩個方法:

《Java核心技術 卷Ⅱ 進階特性(原書第10版)》一2.4.3 修改預設的序列化機制

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

《Java核心技術 卷Ⅱ 進階特性(原書第10版)》一2.4.3 修改預設的序列化機制

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