1. 程式人生 > >java 序列化機制

java 序列化機制

1. 序列化概念:

序列化就是一種用來處理物件流的機制,所謂物件流也就是將物件的內容進行流化,將資料分解成位元組流,以便儲存在檔案中或在網路上傳輸。可以對流化後的物件進行讀寫操作,也可將流化後的物件傳輸於網路之間。序列化是為了解決在對物件流進行讀寫操作時所引發的問題。

序列化分為兩大部分:序列化和反序列化。序列化是這個過程的第一部分,將資料分解成位元組流,以便儲存在檔案中或在網路上傳輸。反序列化就是開啟位元組流並重構物件。物件序列化不僅要將基本資料型別轉換成位元組表示,有時還要恢復資料。恢復資料要求有恢復資料的物件例項 。

2. 實現序列化的情況:

(1)物件序列化可以實現分散式物件。主要應用例如:RMI要利用物件序列化執行遠端主機上的服務,就像在本地機上執行物件時一樣。 

(2)java物件序列化不僅保留一個物件的資料,而且遞迴儲存物件引用的每個物件的資料。可以將整個物件層次寫入位元組流中,可以儲存在檔案中或在網路連線上傳遞。利用物件序列化可以進行物件的"深複製",即複製物件本身及引用的物件本身。序列化一個物件可能得到整個物件序列。 

3. 序列化特性:

(1)如果某個類能夠被序列化,其子類也可以被序列化。

(2)宣告為static和transient型別的成員資料不能被序列化。因為static代表類的狀態, transient代表物件的臨時資料。 

4. 實現序列化與反序列化方式:

(1)實現Serializable介面

即將需要序列化的類實現Serializable介面就可以了,Serializable介面中沒有任何方法,可以理解為一個標記,即表明這個類可以序列化。

(2)自定義序列化

需要自定義序列化和反序列化的類中需要提供以下方法:

private void writeObject(ObjectOutputStream out)
private void readObject(ObjectInputStream in)
private void readObjectNoData()

 writeObject 方法負責寫入特定類的物件的狀態,以便相應的 readObject 方法可以還原它。通過呼叫 out.defaultWriteObject 可以呼叫儲存 Object 的欄位的預設機制。該方法本身不需要涉及屬於其超類或子類的狀態。狀態是通過使用 writeObject 方法或使用 DataOutput 支援的用於基本資料型別的方法將各個欄位寫入 ObjectOutputStream 來儲存的。 
  readObject 方法負責從流中讀取並還原類欄位。它可以呼叫 in.defaultReadObject 來呼叫預設機制,以還原物件的非靜態和非瞬態欄位。defaultReadObject 方法使用流中的資訊來分配流中通過當前物件中相應命名欄位儲存的物件的欄位。這用於處理類發展後需要新增新欄位的情形。該方法本身不需要涉及屬於其超類或子類的狀態。狀態是通過使用 writeObject 方法或使用 DataOutput 支援的用於基本資料型別的方法將各個欄位寫入 ObjectOutputStream 來儲存的。

注:ObjectOutputStream先通過反射在要被序列化的物件的類中查詢有無自定義的writeObject方法,若有,則會優先呼叫自定義的writeObject方法。因為查詢反射方法時使用的是getPrivateMethod,所以自定以的writeObject方法的作用域要被設定為private。通過自定義writeObject和readObject方法可以完全控制物件的序列化與反序列化。

(3)實現介面Externalizable

注:Externalizable介面繼承自Serializable介面,但他們的序列化機制是完全不同的,使用Serializable的所有方式,在反序列化是不會呼叫任何的序列化物件構造器,而使用Externalizable是會呼叫一個無參構造方法的,原因如下: 
Externalizable序列化的過程:使用Externalizable序列化時,在進行反序列化的時候,會重新例項化一個物件,然後再將被反序列化的物件的狀態全部複製到這個新的例項化物件當中去,這也就是為什麼會呼叫構造方法啦,也因此必須有一個無參構造方法供其呼叫,並且許可權是public。

5. 實現Externalizable、Serializable區別:

 Serializable介面 
  · 優點:內建支援 
  · 優點:易於實現 
  · 缺點:佔用空間過大 
  · 缺點:由於額外的開銷導致速度變比較慢 
  Externalizable介面 
  · 優點:開銷較少(程式設計師決定儲存什麼) 
  · 優點:可能的速度提升 
  · 缺點:虛擬機器不提供任何幫助,也就是說所有的工作都落到了開發人員的肩上。 

   注意:如果一個類是可外部化的(Externalizable),那麼Externalizable方法將被用於序列化類的例項,即使這個型別提供了                  Serializable方法: private void writeObject() 、private void readObject()

6.  serialVersionUID生成機制:

如果可序列化類未顯式宣告 serialVersionUID,則序列化執行時將基於類名、介面名、成員方法及屬性等來生成一個64位的雜湊欄位作為該類的預設 serialVersionUID 值,如“Java(TM) 物件序列化規範”中所述。不過,強烈建議 所有可序列化類都顯式宣告 serialVersionUID 值,原因計算預設的 serialVersionUID 對類的詳細資訊具有較高的敏感性,根據編譯器實現的不同可能千差萬別,這樣在反序列化過程中可能會導致意外的 InvalidClassException。因此,為保證 serialVersionUID 值跨不同 java 編譯器實現的一致性,序列化類必須宣告一個明確的 serialVersionUID 值。還強烈建議使用 private 修改器顯示宣告 serialVersionUID(如果可能),原因是這種宣告僅應用於立即宣告類 -- serialVersionUID 欄位作為繼承成員沒有用處。

7.程式碼示例:

情景:採用預設的serialVersionUID進行序列化,序列化完成後我對UserDO的屬性進行修改,然後再反序列化。

序列化

將UserDO屬性sex註釋掉

進行反序列化

解決辦法顯式宣告 serialVersionUID 值: