物件序列化詳解
前言 |
我們在做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這個類變數。即使物件被序列化後,他所對應的類修改了,該物件依然能被正確的反序列化。
其他關於序列化的小知識 |
- 序列化不儲存靜態變數
- 要想父類物件也參與序列化操作,那麼必須要讓父類也實現Serializable介面
- Transient關鍵字,主要是控制變數是否能夠被序列化。如果沒有被序列化的成員變數反序列化後,會被設定成初始值,比如String -> null