序列化:將對象狀態轉換為字節序列(可存儲、傳輸)。
反序列化:從字節序列重建對象。
核心接口:java.io.Serializable(標記接口,無方法)。
import java.io.*;
// 1. 實現 Serializable 接口
class Customer implements Serializable {
private static final long serialVersionUID = 1L; // 強烈建議顯式聲明
private String name;
private int age;
private transient String password; // transient 不參與序列化
public Customer(String name, int age, String password) {
this.name = name; this.age = age; this.password = password;
}
// getter/toString 省略
}
public class SerializationDemo {
private static final String FILE = "customer.ser";
public static void main(String[] args) {
// === 序列化 ===
Customer original = new Customer("張三", 25, "secret123");
serialize(original);
// === 反序列化 ===
Customer restored = deserialize();
System.out.println("原始: " + original);
System.out.println("恢復: " + restored);
// 輸出: password 為 null(被 transient 忽略)
}
static void serialize(Customer obj) {
try (FileOutputStream fos = new FileOutputStream(FILE);
ObjectOutputStream oos = new ObjectOutputStream(fos)) {
oos.writeObject(obj); // 核心方法:遞歸序列化對象圖
System.out.println("序列化完成 -> " + FILE);
} catch (IOException e) {
e.printStackTrace();
}
}
static Customer deserialize() {
try (FileInputStream fis = new FileInputStream(FILE);
ObjectInputStream ois = new ObjectInputStream(fis)) {
return (Customer) ois.readObject(); // 核心方法:需強制轉型
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
}
| 類/接口 | 角色 | 核心方法 |
|---|---|---|
Serializable | 標記接口,聲明類可序列化 | 無 |
ObjectOutputStream | 序列化輸出流 | writeObject(Object) |
ObjectInputStream | 反序列化輸入流 | readObject() |
Externalizable | 完全自定義序列化協議 | writeExternal/readExternal |
InvalidClassException。private static final long serialVersionUID = 1L;(推薦)修飾字段不參與序列化,反序列化時為默認值(null/0/false)。適用於敏感數據(密碼)、臨時緩存、非序列化對象引用。
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject(); // 默認序列化
out.writeObject(encrypt(password)); // 手動寫入加密密碼
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject(); // 默認反序列化
this.password = decrypt((String) in.readObject()); // 手動讀取解密
}
static 字段屬於類,不屬於實例,不序列化。transient 字段顯式排除。父類無 Serializable 但有無參構造器:反序列化時調用父類無參構造器初始化父類字段。
父類無無參構造器:拋 InvalidClassException。
⚠️ 反序列化漏洞(Gadget Chain):惡意構造字節流,利用
readObject/readResolve等魔術方法執行任意代碼(如 Commons Collections、Spring、Fastjson 歷史漏洞)。
ObjectInputFilter 白名單/黑名單類。readObjectNoData() 處理超級類不匹配情況。Java 原生序列化雖然簡單,但性能一般、跨語言差、安全風險高。現代架構中:
serialVersionUID、transient 敏感字段、啟用 ObjectInputFilter、嚴格校驗來源。