Java序列化是指把Java對象轉換爲字節序列的過程,而Java反序列化是指把字節序列恢復爲Java對象的過程。
序列化的使用場景
- 永久性保存對象,保存對象的字節序列到本地文件或者數據庫中
- 通過序列化以字節流的形式使對象在網絡中進行傳遞和接收
- 通過序列化在進程間傳遞對象
序列化的好處
- 實現了數據的持久化,通過序列化可以把數據永久地保存到硬盤上
- 利用序列化實現遠程通信,即在網絡上傳送對象的字節序列
使用Serializable接口實現序列化
package xuliehua;
import java.io.Serializable;
public class Person implements Serializable {
private static final long serialVersionUID = 6839537839090265344L;
private int age;
private String name;
private String sex;
public int getAge() {
return age;
}
public String getName() {
return name;
}
public String getSex() {
return sex;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setSex(String sex) {
this.sex = sex;
}
}
package xuliehua;
import java.io.*;
import java.text.MessageFormat;
public class TestObjSerializeAndDeserialize {
public static void main(String[] args) throws Exception {
SerializePerson();//序列化Person對象
Person p = DeserializePerson();//反序列Perons對象
System.out.println(MessageFormat.format("name={0},age={1},sex={2}",
p.getName(), p.getAge(), p.getSex()));
}
private static void SerializePerson() throws FileNotFoundException, IOException {
Person person = new Person();
person.setName("fate");
person.setAge(25);
person.setSex("男");
// ObjectOutputStream 對象輸出流,將Person對象存儲到E盤的Person.txt文件中,完成對Person對象的序列化操作
ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(
new File("E:/Person.txt")));
oo.writeObject(person);
System.out.println("Person對象序列化成功!");
oo.close();
}
private static Person DeserializePerson() throws Exception, IOException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
new File("E:/Person.txt")));
Person person = (Person) ois.readObject();
System.out.println("Person對象反序列化成功!");
return person;
}
}
結果
Person對象序列化成功!
Person對象反序列化成功!
name=fate,age=25,sex=男
對象序列化包括如下步驟:
- 創建一個對象輸出流,它可以包裝一個其他類型的目標輸出流,如文件輸出流;
- 通過對象輸出流的writeObject()方法寫對象。
對象反序列化的步驟如下:
- 創建一個對象輸入流,它可以包裝一個其他類型的源輸入流,如文件輸入流;
- 通過對象輸入流的readObject()方法讀取對象。
使用Externalizable接口實現序列化
package xuliehua;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
public class PersonNew implements Externalizable {
private static final long serialVersionUID = -7103994996403529702L;
private int age;
private String name;
private String sex;
public PersonNew(){
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public String getSex() {
return sex;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(age);
out.writeObject(name);
out.writeObject(sex);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
age = in.readInt();
name = (String) in.readObject();
sex = (String) in.readObject();
}
}
Externalizable繼承了Serializable,該接口中定義了兩個抽象方法:writeExternal()與readExternal()。
當使用Externalizable接口來進行序列化與反序列化的時候需要開發人員重寫writeExternal()與readExternal()方法。
在使用Externalizable進行序列化的時候,在讀取對象時,會調用被序列化類的無參構造器去創建一個新的對象,然後再將被保存對象的字段的值分別填充到新對象中。
所以,實現Externalizable接口的類必須要提供一個public的無參的構造器。
Externalizable和Serializable接口的區別
- Externalizable繼承自Serializable接口
- 需要我們重寫writeExternal()與readExternal()方法
- 實現Externalizable接口的類必須要提供一個public的無參的構造器
serialVersionUID的作用
其目的是序列化對象版本控制,有關各版本反序列化時是否兼容。Java的序列化機制是通過判斷類的serialVersionUID來驗證版本一致性的。
如果在新版本中這個值修改了,新版本就不兼容舊版本,反序列化時會拋出InvalidClassException異常。
如果修改較小,比如僅僅是增加了一個屬性,我們希望向下兼容,老版本的數據都能保留,那就不用修改;
如果我們刪除了一個屬性,或者更改了類的繼承關係,必然不兼容舊數據,這時就應該手動更新版本號,即SerialVersionUid。
serialVersionUID有兩種顯示的生成方式
一是默認的1L,比如:private static final long serialVersionUID = 1L;
二是根據類名、接口名、成員方法及屬性等來生成一個64位的哈希字段,比如:private static final long serialVersionUID = xxxxL;
在實現這個Serializable 接口的時候,一定要給這個 serialVersionUID 賦值
靜態變量不會被序列化,序列化保存的是對象的狀態,靜態變量屬於類的狀態,因此 序列化並不保存靜態變量。
Transient 關鍵字作用
Transient 關鍵字的作用是控制變量的序列化,在變量聲明前加上該關鍵字,可以阻止該變量被序列化到文件中,在被反序列化後,transient 變量的值被設爲初始值,如 int 型的是 0,對象型的是 null
當一個父類實現序列化,子類自動實現序列化,不需要顯式實現Serializable接口
爲一個沒有實現Serializable接口的父類,編寫一個能夠序列化的子類
1, 父類要有一個無參的constructor;
2, 子類要先序列化自身,然後子類要負責序列化父類的域