天天看點

serialVersionUID 問題處理

serialVersionUID 問題處理

類通過實作 java.io.Serializable 接口以啟用其序列化功能。未實作此接口的類将無法進行序列化或反序列化。可序列化類的所有子類型本身都是可序列化的。

檢視Serializable接口的源代碼可以看到接口是空的,沒有任何的方法或字段,僅用于标示可序列化的語義,如果一個類沒有實作這個接口,想要被序列化的話,就會抛出java.io.NotSerializableException異常。

serialVersionUID 問題處理

Java中還提供了Externalizable接口,也可以實作它來提供序列化能力。

Externalizable繼承自Serializable,該接口中定義了兩個抽象方法:writeExternal()與readExternal()。

當使用Externalizable接口來進行序列化與反序列化的時候需要開發人員重寫writeExternal()與readExternal()方法。否則所有變量的值都會變成預設值。

備注:

Externalizable接口和Serializable接口的差別:

Externalizable繼承了Serializable,該接口中定義了兩個抽象方法:writeExternal()與readExternal()。當使用Externalizable接口來進行序列化與反序列化的時候需要開發人員重寫writeExternal()與readExternal()方法。如果沒有在這兩個方法中定義序列化實作細節,是以輸出的内容為空。還有一點值得注意:在使用Externalizable進行序列化的時候,在讀取對象時,會調用被序列化類的無參構造器去建立一個新的對象,然後再将被儲存對象的字段的值分别填充到新對象中。是以,實作Externalizable接口的類必須要提供一個public的無參的構造器。

transient 關鍵字的作用是控制變量的序列化,在變量聲明前加上該關鍵字,可以阻止該變量被序列化到檔案中,在被反序列化後,transient 變量的值被設為初始值,如 int 型的是 0,對象型的是 null。

深入閱讀參考文獻:

深入分析Java的序列化與反序列化

簡單來說,Java的序列化機制是通過在運作時判斷類的serialVersionUID來驗證版本一緻性的。在進行反序列化時,JVM會把傳來的位元組流中的serialVersionUID與本地相應實體(類)的serialVersionUID進行比較,如果相同就認為是一緻的,可以進行反序列化,否則就會出現序列化版本不一緻的異常。

當實作java.io.Serializable接口的實體(類)沒有顯式地定義一個名為serialVersionUID,類型為long的變量時,Java序列化機制會根據編譯的class自動生成一個serialVersionUID作序列化版本比較用,這種情況下,隻有同一次編譯生成的 class才會生成相同的serialVersionUID 。

如果我們不希望通過編譯來強制劃分軟體版本,即實作序列化接口的實體能夠相容先前版本,未作更改的類,就需要顯式地定義一個名為serialVersionUID,類型為long的變量,不修改這個變量值的序列化實體都可以互相進行串行化和反串行化。

其中阿裡社群Java開發規範也就這一點做了強制要求。

serialVersionUID 問題處理

serialVersionUID是用來驗證版本一緻性的。是以在做相容性更新的時候,不要改變類中serialVersionUID的值。

如果一個類實作了Serializable接口,一定要記得定義serialVersionUID,否則會發生異常。之是以會發生異常,是因為反序列化過程中做了校驗,并且如果沒有明确定義的話,會根據類的屬性自動生成一個,但是隻有同一次編譯生成的class才會生成相同的serialVersionUID,更新後再次編譯的class将無法與上個版本相容而異常。

serialVersionUID 問題處理