java 序列化版本的作用

來源:互聯網
上載者:User
簡單來說,Java的序列化機制是通過在運行時判斷類的serialVersionUID來驗證版本一致性的。在進行還原序列化時,JVM會把傳來的位元組流中的serialVersionUID與本地相應實體(類)的serialVersionUID進行比較,如果相同就認為是一致的,可以進行還原序列化,否則就會出現序列化版本不一致的異常。當實現java.io.Serializable介面的實體(類)沒有顯式地定義一個名為serialVersionUID,類型為long的變數時,Java序列化機制會根據編譯的class自動產生一個serialVersionUID作序列化版本比較用,這種情況下,只有同一次編譯產生的class才會產生相同的serialVersionUID 。如果我們不希望通過編譯來強制劃分軟體版本,即實現序列化介面的實體能夠相容先前版本,未作更改的類,就需要顯式地定義一個名為serialVersionUID,類型為long的變數,不修改這個變數值的序列化實體都可以相互進行序列化和反序列化。 serialVersionUID主要來判斷不同版本的相容性。當在還原序列化的時候,位元組流中的版本號碼,就和serialVersionUID區比較,而產生它的對象。如果你沒有設定,java序列化機制,也自動會給你分配一個。其實這個東西沒必要去考慮!
public interface Serializable類通過實現 java.io.Serializable 介面以啟用其序列化功能。未實現此介面的類將無法使其任何狀態序列化或還原序列化。可序列化類別的所有子類型本身都是可序列化的。序列化介面沒有方法或欄位,僅用於標識可序列化的語義。

要允許不可序列化類別的子類型序列化,可以假定該子類型負責儲存和恢複超類型的公用 (public)、受保護的 (protected) 和(如果可訪問)包 (package) 欄位的狀態。僅在子類型擴充的類有一個可訪問的無參數構造方法來初始化該類的狀態時,才可以假定子類型有此職責。如果不是這種情況,則聲明一個類為可序列化類別是錯誤的。該錯誤將在運行時檢測到。

在還原序列化過程中,將使用該類的公用或受保護的無參數構造方法初始化不可序列化類別的欄位。可序列化的子類必須能夠訪問無參數構造方法。可序列化子類的欄位將從該流中恢複。  

當遍曆一個圖形時,可能會遇到不支援 Serializable 介面的對象。在此情況下,將拋出 NotSerializableException,並將標識不可序列化對象的類。  

在序列化和還原序列化過程中需要特殊處理的類必須使用下列準確簽名來實現特殊方法:  
 private void writeObject(java.io.ObjectOutputStream out)
  throws IOException
 private void readObject(java.io.ObjectInputStream in)
  throws IOException, ClassNotFoundException;
 private void readObjectNoData()  
  throws ObjectStreamException;
 writeObject 方法負責寫入特定類的對象的狀態,以便相應的 readObject 方法可以恢複它。通過調用 out.defaultWriteObject 可以調用儲存 Object 的欄位的預設機制。該方法本身不需要涉及屬於其超類或子類的狀態。通過使用 writeObject 方法或使用 DataOutput 支援的用於基礎資料型別 (Elementary Data Type)的方法將各個欄位寫入 ObjectOutputStream,狀態可以被儲存。  

readObject 方法負責從流中讀取並恢複類欄位。它可以調用 in.defaultReadObject 來調用預設機制,以恢複對象的非靜態和非瞬態欄位。defaultReadObject 方法使用流中的資訊來分配流中通過當前對象中相應指定欄位儲存的對象的欄位。這用於處理類演化後需要添加新欄位的情形。該方法本身不需要涉及屬於其超類或子類的狀態。通過使用 writeObject 方法或使用 DataOutput 支援的用於基礎資料型別 (Elementary Data Type)的方法將各個欄位寫入 ObjectOutputStream,狀態可以被儲存。  

在序列化流不列出給定類作為將被還原序列化對象的超類的情況下,readObjectNoData 方法負責初始化特定類的對象狀態。這在接收方使用的還原序列化執行個體類的版本不同於發送方,並且接收者版本擴充的類不是寄件者版本擴充的類時發生。在序列化流已經被篡改時也將發生;因此,不管源流是“敵意的”還是不完整的,readObjectNoData 方法都可以用來正確地初始化還原序列化的對象。  

將對象寫入流時需要指定要使用的替代對象的可序列化類別,應使用準確的簽名來實現此特殊方法:
 ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
 此 writeReplace 方法將由序列化調用,前提是如果此方法存在,而且它可以通過被序列化對象的類中定義的一個方法訪問。因此,該方法可以擁有私人 (private)、受保護的 (protected) 和包私人 (package-private) 訪問。子類對此方法的訪問遵循 java 訪問規則。  

在從流中讀取類的一個執行個體時需要指定替代的類應使用的準確簽名來實現此特殊方法。
 ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
 此 readResolve 方法遵循與 writeReplace 相同的調用規則和訪問規則。

序列化運行時使用一個稱為 serialVersionUID 的版本號碼與每個可序列化類別相關聯,該序號在還原序列化過程中用於驗證序列化對象的寄件者和接收者是否為該對象載入了與序列化相容的類。如果接收者載入的該對象的類的 serialVersionUID 與對應的寄件者的類的版本號碼不同,則還原序列化將會導致 InvalidClassException。可序列化類別可以通過聲明名為 "serialVersionUID" 的欄位(該欄位必須是靜態 (static)、最終 (final) 的 long 型欄位)顯式聲明其自己的 serialVersionUID:
 ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
 如果可序列化類別未顯式聲明 serialVersionUID,則序列化運行時將基於該類的各個方面計算該類的預設 serialVersionUID 值,如“Java(TM) 對象序列化規範”中所述。不過,強烈建議 所有可序列化類別都顯式聲明 serialVersionUID 值,原因是計算預設的 serialVersionUID 對類的詳細資料具有較高的敏感性,根據編譯器實現的不同可能千差萬別,這樣在還原序列化過程中可能會導致意外的 InvalidClassException。因此,為保證 serialVersionUID 值跨不同 java 編譯器實現的一致性,序列化類別必須聲明一個明確的 serialVersionUID 值。還強烈建議使用 private 修飾符顯示聲明 serialVersionUID(如果可能),原因是這種聲明僅應用於直接聲明類 -- serialVersionUID 欄位作為繼承成員沒有用處。數組類不能聲明一個明確的 serialVersionUID,因此它們總是具有預設的計算值,但是數組類沒有匹配 serialVersionUID 值的要求。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.