Java 平台允許在內存中創建可復用的對象,但這些對象的生命週期通常不超過 JVM。現實應用中,往往需要在 JVM 停止後保存(持久化)指定對象,並在未來重新讀取。Java 對象序列化正是實現此功能的機制:保存對象時將其狀態轉為一組字節,未來再將字節組裝回對象。注意,序列化保存的是對象的「狀態」(即成員變量),不關注靜態變量。
除了持久化,RMI(遠程方法調用)或網絡傳輸對象時也會用到序列化。Java 序列化 API 提供了標準機制,簡單易用。
只要類實現 java.io.Serializable 接口即可被序列化。以下以 Person 類為例:
public enum Gender { MALE, FEMALE }
public class Person implements Serializable {
private String name;
private Integer age;
private Gender gender;
// 構造器、getter/setter、toString() 省略
}
序列化與反序列化程序:
public class SimpleSerial {
public static void main(String[] args) throws Exception {
File file = new File("person.out");
ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream(file));
Person person = new Person("John", 101, Gender.MALE);
oout.writeObject(person);
oout.close();
ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));
Object newPerson = oin.readObject();
oin.close();
System.out.println(newPerson);
}
}
輸出:arg constructor [John, 31, MALE]。注意反序列化時未調用構造器,而是直接從字節還原對象。讀取時 CLASSPATH 必須包含 Person.class,否則拋出 ClassNotFoundException。
ObjectOutputStream 在寫入對象時會檢查類型:若為 String、數組、Enum 或實現 Serializable 的對象,則進行序列化,否則拋出 NotSerializableException。
僅實現 Serializable 而無其他處理時,使用默認機制。序列化對象時會遞歸序列化其引用的所有對象,若對象圖複雜(如嵌套容器),開銷較大。
聲明為 transient 的字段會被默認序列化機制忽略。例如將 age 標記為 transient,反序列化後該字段為 null。
在類中私有定義這兩個方法可自定義序列化邏輯。例如對 transient 字段手動寫入/讀取:
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeInt(age);
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
age = in.readInt();
}
這兩個方法均為 private,通過反射被調用。
實現 Externalizable 後,基於 Serializable 的機制失效,序列化細節完全由程序員控制。必須實現 writeExternal 和 readExternal 方法,且類需提供公共無參構造器。反序列化時會先調用無參構造器創建對象,再填充字段。
用於保護單例模式:反序列化時若返回單例實例,可確保單例唯一性。無論實現 Serializable 還是 Externalizable,讀取對象時都會調用 readResolve(),其返回對象會替換新創建的對象。