從serialVersionUID到Java序列化

在咱們的項目開發過程當中,每每會有這樣的要求,對於須要序列化的類,在實現serializable接口的同時,要求在類中定義serialVersionUID.java

public static final long serialVersionUID = -11222222122xxxxL;
複製代碼

這時咱們每每會產生疑問,這個serialVersionUID是幹什麼用的呢?在討論serialVersionUID時,咱們必須結合Java的序列化對其進行分析.bash

衆所周知,Java的序列化機制的主要做用在於兩點:網絡

  1. 方便對內存中的對象數據進行持久化
  2. 便於對象在網絡中的傳輸,常見應用如RPC

那麼在使用序列化機制保存對象的時候,咱們就須要考慮一個問題:隨着應用程序的演進,序列化對象自己的定義也在發生更改,那麼舊版本的程序是否還能向後兼容新的序列化對象?或者,新版程序還可否向前兼容去處理老版本的對象?spa

在類中定義serialVersionUID靜態域就是爲了Java序列化的版本兼容,爲何這麼說呢? 首先,讓咱們回顧一下Java序列化的知識:版本控制

當序列化存儲一個對象時,這個對象所屬的類也必須存儲.這個類的描述包含了:code

  • 類名
  • 序列化的版本惟一的ID,也是數據域類型和方法簽名的指紋(本文所說的serialVersionUID就是這個)
  • 描述序列化方法的標誌集
  • 對數據域的描述

到這裏,咱們知道serialVersionUID就是序列化對象的指紋,而它的值是經過對類,超類,接口,域類型和方法簽名按照規範方式排序,而後進行SHA獲得的20字節長度的數據. 所以,理論上來講當對象所屬的類的定義發生變化時,其serialVersionUID必定會發生變化,可是因爲序列化機制只使用SHA碼的前8個字節,所以不是必定發生變化,可是概率仍是很是大的.對象

而序列化機制在讀入一個對象時,會使用它的指紋與它所屬的類的當前指紋進行比對,若是它們不匹配,就說明這個類的定義在該對象被寫出以後發生過變化,從而會拋出java.io.InvalidClassException異常.排序

照這樣來講,豈不是無法進行版本兼容了?固然不會.接口

Java核心技術中是這樣解釋的:內存

若是一個類具備名爲serialVersionUID的靜態數據成員,它就再也不須要人工地計算其指紋,只需直接使用這個值

一旦這個靜態數據成員被置於某個類的內部,那麼序列化系統就能夠讀入這個類的對象的不一樣版本

到這裏,添加serialVersionUID靜態域的做用已經很清楚了.經過這一措施,可使Java的序列化更好地進行版本控制,避免拋出java.io.InvalidClassException異常,雖然當出現數據的版本不一致時,可能會致使數據的一部分丟失,但至少保證了服務的可用.

當序列化對象與類的版本不一致時,Java序列化是如何工做的?

若是這個類只有方法發生了變化,那麼在讀入新對象數據時是不會有任何問題的.可是若是字段域發生了變化,那麼可能出現問題,例如可能類中的字段減小或者類型的改變等等.

  1. 若是被序列化的對象具備當前版本的類中沒有的成員字段(非transient,非static),那麼忽略這些額外的數據
  2. 若是當前版本的類中具備序列化對象中沒有的域,賦默認值
相關文章
相關標籤/搜索