1. 程式人生 > >Java物件的serialVersionUID在序列化和反序列化的用途

Java物件的serialVersionUID在序列化和反序列化的用途

本部落格主要轉自如下連結

http://blog.csdn.net/javazejian/article/details/52665164

這篇文章寫的不錯,但是有些地方我估計博主沒有親自測試,所以有些地方我親測後發現其實他說的不對,大家可以先看看他寫的,然後再看看我下面說的具體哪裡不對了

先說說serialVersionUID的用途:

其實一個類要實現序列化和反序列化,不宣告serialVersionUID是可行的,serialVersionUID主要是輔助序列化和反序列化,序列化和反序列化的時候,serialVersionUID必須一致,反序列化才會成功。具體的序列化過程是這樣的:序列化操作的時候系統會把當前類的serialVersionUID寫入到序列化檔案中,當反序列化時系統會去檢測檔案中的serialVersionUID,判斷它是否與當前類的serialVersionUID一致,如果一致就說明序列化類的版本與當前類版本是一樣的,可以反序列化成功,否則失敗。報出如下UID錯誤:

Exception in thread "main" java.io.InvalidClassException: com.zejian.test.Client; 
local class incompatible: stream classdesc serialVersionUID = -2083503801443301445, 
local class serialVersionUID = -4083503801443301445

原博文中主要錯在這句話  “因此強烈建議指定serialVersionUID,這樣的話即使微小的變化也不會導致crash的出現,如果不指定的話只要這個檔案多一個空格,系統自動生成的UID就會截然不同的,反序列化也就會失敗

親測發現,當不指定serialVersionUID的時候,將物件序列化到檔案中後,如果僅僅在物件檔案中添加了一些空格或者換行,反序列化仍然會成功,而並不是那篇部落格中說的加個空格就會反序列化失敗。原因應該是serialVersionUID的生成方法是基於物件的一些屬性生成的,而跟這個物件檔案中的空格換行等是沒有關係的。

我試著在沒有指定的serialVersionUID的情況下,序列化後,在物件中添加了一個屬性,並對之前生成的序列化後的檔案進行反序列化,發現失敗了,拋了上述說的異常,這是因為在物件中添加了屬性後,由於沒有指定serialVersionUID,所以系統會自動根據這個物件的屬性等產生一個新的serialVersionUID,這就導致這個serialVersionUID與序列化檔案中的serialVersionUID不一致,從而反序列化失敗

接著,我為物件加了serialVersionUID屬性,在intellij idea中為一個物件加serialVersionUID的方法如下:

Setting->Inspections->Serialization issues->Serializable class without ’serialVersionUID’
選中以上後,在你的class中:游標定位在類名前,按 Alt+Enter 就會提示自動建立 serialVersionUID 了。
在添加了serialVersionUID的物件中,我重新進行了序列化,序列化後,我在物件中添加了一個新的屬性,也修改了原有的屬性,但是serialVersionUID沒有變,所以序列化檔案中的serialVersionUID和物件檔案中的serialVersionUID一致,反序列化都成功,但是隻會反序列化成功匹配上的屬性值,沒有匹配上的都會為null。如果在序列化後修改了serialVersionUID的值,則反序列化會失敗,丟擲上述異常。

下面是上述部落格中測試的例子,我進行了點修改方便測試:

被序列化物件Client:注意,這個物件中的幾個object相關方法是用來自定義序列化和反序列化過程的,我們自定義哪些屬性需要序列化,上述測試我把object相關方法都註釋掉了。

package com.sogou.study.serialize;

/**
 * Created by denglinjie on 2016/9/29.
 */
import java.io.IOException;
import java.io.ObjectStreamException;
import java.io.Serializable;

public class Client implements Serializable{

    /**
     * 生成序列號標識
     */
    private static final long serialVersionUID = -2083503801443301445L;

    private int id;

    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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


    /**
     * 序列化時,
     * 首先系統會先呼叫writeReplace方法,在這個階段,
     * 可以進行自己操作,將需要進行序列化的物件換成我們指定的物件.
     * 一般很少重寫該方法
     * @return
     * @throws ObjectStreamException
     */
    private Object writeReplace() throws ObjectStreamException {
        System.out.println("writeReplace invoked");
        return this;
    }
    /**
     *接著系統將呼叫writeObject方法,
     * 來將物件中的屬性一個個進行序列化,
     * 我們可以在這個方法中控制住哪些屬性需要序列化.
     * 這裡只序列化name屬性
     * @param out
     * @throws IOException
     */
    private void writeObject(java.io.ObjectOutputStream out) throws IOException {
        System.out.println("writeObject invoked");
        //這裡自定義只序列化了物件的name屬性到檔案中
        out.writeObject(this.name == null ? "zejian" : this.name);
    }

    /**
     * 反序列化時,系統會呼叫readObject方法,將我們剛剛在writeObject方法序列化好的屬性,
     * 反序列化回來.然後通過readResolve方法,我們也可以指定系統返回給我們特定的物件
     * 可以不是writeReplace序列化時的物件,可以指定其他物件.
     * @param in
     * @throws IOException
     * @throws ClassNotFoundException
     */
    private void readObject(java.io.ObjectInputStream in) throws IOException,
            ClassNotFoundException {
        System.out.println("readObject invoked");
        //由於序列化的時候自定義只序列化了name屬性,所以這裡讀出來的object就是name的值,可以直接轉換成String
        this.name = (String) in.readObject();
        System.out.println("got name:" + name);
    }


    /**
     * 通過readResolve方法,我們也可以指定系統返回給我們特定的物件
     * 可以不是writeReplace序列化時的物件,可以指定其他物件.
     * 一般很少重寫該方法
     * @return
     * @throws ObjectStreamException
     */
    private Object readResolve() throws ObjectStreamException {
        System.out.println("readResolve invoked");
        return this;
    }
}

序列化類
package com.sogou.study.serialize;

/**
 * Created by denglinjie on 2016/9/29.
 */

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;

public class SerializeTest {

    public static void main(String[] args) throws Exception {

        //把物件序列化到檔案
        Client client = new Client();
        client.setId(11);
        client.setName("client");

        ObjectOutputStream oo = new ObjectOutputStream
                (new FileOutputStream("D:\\test\\file1.txt"));
        oo.writeObject(client);
        oo.close();
    }
}

反序列化類:
package com.sogou.study.serialize;

/**
 * Created by denglinjie on 2016/9/29.
 */

import java.io.FileInputStream;
import java.io.ObjectInputStream;

public class UnSerializeTest {

    public static void main(String[] args) throws Exception {
        //反序列化到記憶體
        ObjectInputStream oi = new ObjectInputStream
                (new FileInputStream("D:\\test\\file1.txt"));
        Client c_back = (Client) oi.readObject();
        System.out.println(c_back.getName());
        System.out.println(c_back.getId());
        oi.close();

    }
}