深入java 序列化和反序列化基本用法

這篇文章總結一下java序列化和反序列化基礎用法,序列化和反序列化注意的一些細節問題。更多的源代碼請訪問我的github:https://github.com/yangsheng20080808/deepIntoJava

原創不易,轉載請聲明出處。

本文分爲3大部分

  • 我們爲什麼需要序列化和反序列化
  • java序列化和反序列化基本用法
  • 版本號對於序列化和反序列化的影響

我們爲什麼需要序列化和反序列化

Java平臺允許我們在內存中創建可複用的Java對象,但一般情況下,只有當JVM處於運行時,這些對象纔可能存在,這些對象的生命週期不會比JVM的生命週期更長。但在現實應用中,就可能要求在JVM停止運行之後能夠保存(持久化)指定的對象,並在將來重新讀取被保存的對象。Java對象序列化就能夠幫助我們實現該功能。
1. 使用Java對象序列化,在保存對象時,會把其狀態保存爲一組字節,在未來,再將這些字節組裝成對象。必須注意地是,對象序列化保存的是對象的”狀態”,即對象的成員變量。由此可知,對象序列化不會關注類中的靜態變量,也就是由static聲明的字段
2. 除了在持久化對象時會用到對象序列化之外,當使用RMI(遠程方法調用),或在網絡中傳遞對象時,都會用到對象序列化。Java序列化API爲處理對象序列化提供了一個標準機制,該API簡單易用。

一句話說白了:我們有一個需要將某個對象保存很久,而且過了一段時間我們還可以將這個對象還原的場景。

java序列化和反序列化基本用法

很經常見到很多的類都實現了Serializable這個接口,這個接口是提供類對象序列化和反序列化的接口,我們直接舉例子來吧:
這裏寫圖片描述

運行結果
這裏寫圖片描述

如果我們在序列化完成後,更改了版本(升級爲2L:serialVersionUID = 2L),類中其他的屬性都沒有改變,反序列化會失敗:
這裏寫圖片描述

serialVersionUID的作用: serialVersionUID 用來表明類的不同版本間的兼容性。如果你修改了此類, 要修改此值。否則以前用老版本的類序列化的類恢復時會出錯。Java的序列化機制是通過在運行時判斷類的serialVersionUID來驗證版本一致性的。在進行反序列化時,JVM會把傳來 的字節流中的serialVersionUID與本地相應實體(類)的serialVersionUID進行比較,如果相同就認爲是一致的,可以進行反序 列化,否則就會出現序列化版本不一致的異常。

假如我們的版本號沒有改變,但是類增加了一些屬性,還是可以兼容的:例子
這裏寫圖片描述

直接將我們的對象反序列化:
這裏寫圖片描述
還是可以兼容,因爲我反序列化的時候在序列化文件中讀取到的兩個參數作爲構造函數的參數,反序列化還是成功。

假如這個時候我們將參數只有兩個(上一個版本的構造函數)的構造函數註釋掉:運行就會報錯了:
這裏寫圖片描述

所以如果我們希望升級版本後可以兼容原來的版本,第一:不要改動serialVersionUID;第二:不要刪除上一個版本的構造函數。

版本號對於序列化和反序列化的影響

設置 serialVersionUID默認的生成方式: private static final long serialVersionUID = 1L;

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

爲了在反序列化時,確保類版本的兼容性,最好在每個要序列化的類中加入 private static final long serialVersionUID這個屬性,而且同時具體數值自己定義。這樣,即使某個類在與之對應的對象 已經序列化出去後做了修改,該對象依然可以被正確反序列化,可以向下兼容。

否則,如果不顯式定義該屬性,這個屬性值將由JVM根據類的相關信息計算,而修改後的類的計算 結果與修改前的類的計算結果往往不同,從而造成對象的反序列化因爲類版本不兼容而失敗。

不顯式定義這個屬性值的另一個壞處是,不利於程序在不同的JVM之間的移植。因爲不同的編譯器實現該屬性值的計算策略可能不同,從而造成雖然類沒有改變,但是因爲JVM不同,可能出現因類版本不兼容而無法正確反序列化的現象出現。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章