1. 程式人生 > >Java I/O---序列化接口Serializable

Java I/O---序列化接口Serializable

可能 格式 數值 ext round pan exc write sde

1.JDK API 中關於Serializable的描述

    public interface Serializable

類通過實現 java.io.Serializable 接口以啟用其序列化功能。未實現此接口的類將無法使其任何狀態序列化或反序列化。可序列化類的所有子類型本身都是可序列化的。序列化接口沒有方法或字段,僅用於標識可序列化的語義。

序列化運行時使用一個稱為 serialVersionUID 的版本號與每個可序列化類相關聯,該序列號在反序列化過程中用於驗證序列化對象的發送者和接收者是否為該對象加載了與序列化兼容的類。如果接收者加載的該對象的類的 serialVersionUID 與對應的發送者的類的版本號不同,則反序列化將會導致

InvalidClassException。可序列化類可以通過聲明名為serialVersionUID 的字段(該字段必須是靜態 (static)、最終 (final) 的 long 型字段)顯式聲明其自己的 serialVersionUID

  1 ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;

如果可序列化類未顯式聲明 serialVersionUID,則序列化運行時將基於該類的各個方面計算該類的默認 serialVersionUID 值如“Java(TM) 對象序列化規範”中所述。不過,強烈建議

所有可序列化類都顯式聲明 serialVersionUID 值,原因是計算默認的 serialVersionUID 對類的詳細信息具有較高的敏感性,根據編譯器(版本)實現的不同可能千差萬別,這樣在反序列化過程中可能會導致意外的 InvalidClassException因此,為保證 serialVersionUID 值跨不同 java 編譯器實現的一致性,序列化類必須聲明一個明確的 serialVersionUID 值。還強烈建議使用 private 修飾符顯示聲明 serialVersionUID(如果可能),原因是這種聲明僅應用於直接聲明類 -- serialVersionUID 字段作為繼承成員沒有用處。數組類不能聲明一個明確的 serialVersionUID,因此它們總是具有默認的計算值,但是數組類沒有匹配 serialVersionUID 值的要求。

下面是一個舉例:

  1 /*
  2  * Person類的對象如果需要序列化,就需要實現Serializable標記接口。
  3  * 該接口給需要序列化的類,提供了一個序列版本號。serialVersionUID.
  4  * 該版本號的目的在於驗證序列化的對象和對應類是否版本匹配。
  5  *
  6  */
  7 public class Person implements Serializable {
  8 
  9 	/*
 10 	 * 給類顯示聲明一個序列版本號。
 11 	 */
 12 	private static final long serialVersionUID = 12324556L;
 13 	private static String name;
 14 	private transient/*瞬態*/ int age;
 15 
 16 	public Person() {
 17 		super();
 18 
 19 	}
 20 
 21 	public Person(String name, int age) {
 22 		super();
 23 		this.name = name;
 24 		this.age = age;
 25 	}
 26 
 27 	public String getName() {
 28 		return name;
 29 	}
 30 	public void setName(String name) {
 31 		this.name = name;
 32 	}
 33 	public int getAge() {
 34 		return age;
 35 	}
 36 	public void setAge(int age) {
 37 		this.age = age;
 38 	}
 39 
 40 	@Override
 41 	public String toString() {
 42 		return "Person [name=" + name + ", age=" + age + "]";
 43 	}
 44 
 45 
 46 }

2.serialVersionUID(串行化版本統一標識符)的作用

在Java中,軟件的兼容性是一個大問題,尤其在使用到對象串行性的時候,那麽在某一個對象已經被串行化了,可是這個對象又被修改後重新部署了,那麽在這種情況下, 用老軟件來讀取新文件格式雖然不是什麽難事,但是有可能丟失一些信息。

serialVersionUID來解決這些問題,默認 serialVersionUID 值是基於該類的各個方面計算的,理論上是一一映射關系,即唯一性。如果UID不一樣,無法實現發序列化,會得到異常InvalidClassException

Java串行化機制定義的文件格式似乎很脆弱,只要稍微改動一下類的定義,原來保存的對象就可能無法讀取。例如,下面是一個簡單的類定義:

  1 public class Save implements Serializable
  2 {
  3     String name;
  4 
  5     public void save() throws IOException
  6     {
  7         FileOutputStream f = new FileOutputStream("foo");
  8         ObjectOutputStream oos = new ObjectOutputStream(f);
  9         oos.writeObject(this);
 10         oos.close();
 11     }
 12 }

如果在這個類定義中增加一個域,例如final int val = 7;,再來讀取原來保存的對象,就會出現下面的異常:

  1 java.io.InvalidClassException:
  2 Save; local class incompatible:
  3 stream classdesc serialVersionUID = -2805284943658356093,
  4 local class serialVersionUID = 3419534311899376629

上例異常信息中的數字串表示類定義裏各種屬性的編碼值:
●類的名字(Save)。
●域的名字(name)。
●方法的名字(Save)。
●已實現的接口(Serializable)。
動上述任意一項內容(無論是增加或刪除),都會引起編碼值變化,從而引起類似的異常警報。這個數字序列稱為“串行化版本統一標識符”(serial version universal identifier),簡稱UID。解決這個問題的辦法是在類裏面新增一個域serialVersionUID,強制類仍舊使用原來的UID。新增的域必須是:
●static:該域定義的屬性作用於整個類,而非特定的對象。
●final:保證代碼運行期間該域不會被修改。
●long:它是一個64位的數值。
也就是說,顯示的serialVersionUID必須定義成下面這種形式:static final long serialVersionUID=-2805284943658356093L;。其中數字後面加上的L表示這是一個long值。

2017-12-31

JAVA 對象序列化(一)——Serializable

serialVersionUID系列化和使用

Java I/O---序列化接口Serializable