標籤:
序列化是幹什麼的
簡單說就是為了儲存在記憶體中的各種對象的狀態(也就是執行個體變數,不是方法),並且可以把儲存的對象狀態再讀出來。雖然你可以用你自己的各種各樣的方法來保 存object states,但是Java給你提供一種應該比你自己好的儲存對象狀態的機制,那就是序列化。
什麼情況下需要序列化
- 當你想把的記憶體中的對象狀態儲存到一個檔案中或者資料庫中時候;
- 當你想用通訊端在網路上傳送對象的時候;
- 當你想通過RMI傳輸對象的時候;
序列化的幾種方式
在Java中socket傳輸資料時,資料類型往往比較難選擇。可能要考慮頻寬、跨語言、版本的相容等問題。比較常見的做法有兩種:一是把對象封裝成JSON字串傳輸,二是採用java對象的序列化和還原序列化。隨著Google工具protoBuf的開源,protobuf也是個不錯的選擇。對JSON,Object Serialize,ProtoBuf 做個對比。
Object Serialize
Java的序列化機制是通過在運行時判斷類的serialVersionUID來驗證版本一致性的。在進行還原序列化時,JVM會把傳來的位元組流中的serialVersionUID與本地相應實體(類)的serialVersionUID進行比較,如果相同就認為是一致的,可以進行還原序列化,否則就會出現序列化版本不一致的異常。
serialVersionUID 用來表明類的不同版本間的相容性。有兩種產生方式:
- 一個是預設的1L,比如:private static final long serialVersionUID = 1L;
- 一個是根據類名、介面名、成員方法及屬性等來產生一個64位的雜湊欄位,比如: private static final long serialVersionUID = xxxxL;
下面來討論Java類中為什麼需要重載 serialVersionUID 屬性?
當兩個進程在進行遠程通訊時,彼此可以發送各種類型的資料。無論是何種類型的資料,都會以二進位序列的形式在網路上傳送。發送方需要把這個Java對象轉換為位元組序列,才能在網路上傳送;接收方則需要把位元組序列再恢複為Java對象。
- 把Java對象轉換為位元組序列的過程稱為對象的序列化。
- 把位元組序列恢複為Java對象的過程稱為對象的還原序列化。
對象的序列化主要有兩種用途:(1)把對象的位元組序列永久地儲存到硬碟上,通常存放在一個檔案中; (2)在網路上傳送對象的位元組序列;
java.io.ObjectOutputStream代表對象輸出資料流,它的writeObject(Object obj)方法可對參數指定的obj對象進行序列化,把得到的位元組序列寫到一個目標輸出資料流中。
java.io.ObjectInputStream代表對象輸入資料流,它的readObject()方法從一個源輸入資料流中讀取位元組序列,再把它們還原序列化為一個對象,並將其返回。
只有實現了Serializable和Externalizable介面的類的對象才能被序列化。Externalizable介面繼承自Serializable介面,實現Externalizable介面的類完全由自身來控制序列化的行為,而僅實現Serializable介面的類可以採用預設的序列化方式 。
凡是實現Serializable介面的類都有一個表示序列化版本標識符的靜態變數:private static final long serialVersionUID;
類的serialVersionUID的預設值完全依賴於Java編譯器的實現,對於同一個類,用不同的Java編譯器編譯,有可能會導致不同的serialVersionUID,也有可能相同。為了提高serialVersionUID的獨立性和確定性,強烈建議在一個可序列化類別中顯示的定義serialVersionUID,為它賦予明確的值。顯式地定義serialVersionUID有兩種用途:
- 在某些場合,希望類的不同版本對序列化相容,因此需要確保類的不同版本具有相同的serialVersionUID;在某些場合,不希望類的不同版本對序列化相容,因此需要確保類的不同版本具有不同的serialVersionUID。
- 當你序列化了一個類執行個體後,希望更改一個欄位或添加一個欄位,不設定serialVersionUID,所做的任何更改都將導致無法反序化舊有執行個體,並在還原序列化時拋出一個異常。如果你添加了serialVersionUID,在反序列舊有執行個體時,新添加或更改的欄位值將設為初始化值(對象為null,基本類型為相應的初始預設值),欄位被刪除將不設定。
JSON化 Google ProtoBuf
Java序列化(Serializable)與還原序列化