1. 程式人生 > >物件序列化詳解

物件序列化詳解

前言

我們在做web專案的時候,在網路傳輸中,序列化是繞不過去的重要一環。今天就來總結一下序列化到底能為我們做些什麼

概念

序列化機制:允許把記憶體中的Java物件轉換成平臺無關的二進位制流,從而允許把這種二進位制流持久地儲存在磁碟上,通過網路將這種二進位制流傳輸到另一個網路節點。其他程式一旦獲得了這種二進位制流(無論磁碟還是網路),都可以將這種二進位制流恢復成原來的Java物件。

如何實現一個序列化的操作

1、實現Serializable介面
2、呼叫ObjectInputStream
3、呼叫ObjectOutputStream

定義一個person類:

public class Person implements Serializable {
    private static final long serialVersionUID = -5183762729469760432L;
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public
int getAge() { return age; } public void setAge(int age) { this.age = age; } private int age; public Person(String name,int age){ this.name=name; this.age=age; } public String toString(){ return "Person{"+ "name="
+name+ ",age="+age+ '}'; } }

客戶端使用ObjectOutSteam序列化物件成為一個二進位制檔案,使用ObjectInputstream反序列化二進位制檔案,成為java物件

public class WriteObject {
    public static void main(String[] args)  {
        SerializePerson();
        DeSerializePerson();
    }

    private static void SerializePerson(){
        try {
            ObjectOutputStream oos= new ObjectOutputStream(new FileOutputStream("person"));
            Person person=new Person("vincent",26);
            oos.writeObject(person);
            System.out.println("序列化成功");
            oos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void DeSerializePerson(){
        try {
            ObjectInputStream ois= new ObjectInputStream(new FileInputStream("person"));
            Person per=(Person) ois.readObject();
            System.out.println(per);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
問題

我們見過很多可序列化的類中都有一一個私有的屬性:serialVersionUID ,作用是什麼

 private static final long serialVersionUID = -5183762729469760432L;

我們做個小實驗,先執行序列化一個物件,然後把上邊這行程式碼註釋掉,在進行反序列化操作,會報出下邊這個錯誤

java.io.InvalidClassException: com.SerializableDemo.Person; local class incompatible: stream classdesc serialVersionUID = -5183762729469760432, local class serialVersionUID = -2791480941090440504
    at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:621)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
    at com.SerializableDemo.WriteObject.DeSerializePerson(WriteObject.java:26)
    at com.SerializableDemo.WriteObject.main(WriteObject.java:8)

serialVersionUID 作用:反序列化Java物件時,必須提供該物件的class檔案,隨著專案的升級,系統的class檔案也會升級,就會出現兩個class檔案的相容問題。Java序列化機制可以通過判定serialVersionUID 的值來判定兩個class檔案是否是一個版本,上邊的報錯就是由於也就是修改過後的class,和檔案流中的class不相容了,出於安全機制考慮,程式丟擲了錯誤,並且拒絕載入。如果沒有為指定的class配置serialVersionUID,那麼java編譯器會自動給這個class進行一個摘要演算法,類似於指紋演算法,只要這個檔案有任何改動,得到的UID就會截然不同的,可以保證在這麼多類中,這個編號是唯一的。

所以說為了在反序列化時確保序列化版本的相容性,我們最好自己在類中加入private static final long serialVersionUID這個類變數。即使物件被序列化後,他所對應的類修改了,該物件依然能被正確的反序列化。

其他關於序列化的小知識
  1. 序列化不儲存靜態變數
  2. 要想父類物件也參與序列化操作,那麼必須要讓父類也實現Serializable介面
  3. Transient關鍵字,主要是控制變數是否能夠被序列化。如果沒有被序列化的成員變數反序列化後,會被設定成初始值,比如String -> null