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();
}
}