Java 序列化 是將對象狀態轉換為位元組流以便持久化存儲或網絡傳輸的核心機制。本文將系統講解序列化概念目的、Serializable 介面實現流程、ObjectOutputStream/InputStream 核心 API、transient 關鍵字與安全防禦、serialVersionUID 版本控制、自定義序列化策略、以及主流替代方案對比,構建完整知識體系。
public class Student implements Serializable {
private static final long serialVersionUID = 1L; // 強烈建議顯式聲明
private int id;
private String name;
private int age;
private String department;
// 構造器、getter/setter、toString...
}
Serializable 無任何方法,僅作標記,告訴 JVM「該類允許序列化」。
Student stu = new Student(981036, "chb", 23, "CSD");
try (FileOutputStream fos = new FileOutputStream("data.ser");
ObjectOutputStream oos = new ObjectOutputStream(fos)) {
oos.writeObject(stu); // 核心方法:遞歸寫入對象圖
}
try (FileInputStream fis = new FileInputStream("data.ser");
ObjectInputStream ois = new ObjectInputStream(fis)) {
Student restored = (Student) ois.readObject(); // 還原對象圖
System.out.println(restored);
}
| 類別 | 示例 | 說明 |
|---|---|---|
| 非 static、非 transient 實例字段 | id, name, age | 核心數據,自動序列化 |
| static 靜態字段 | static int count | 屬於類,不屬於對象,**不序列化** |
| transient 字段 | transient String pwd | **顯式排除**,反序列化為默認值 (null/0/false) |
| 方法、構造器 | getName() | 不屬於對象狀態,**不序列化** |
| 引用對象 | Address address | 若 Address 也實現 Serializable,遞歸序列化 |
Thread、FileInputStream、Socket、Connection 等持有系統資源的對象無法序列化,必須標記 transient,否則拋 NotSerializableException。
密碼、Token、銀行卡號等敏感字段 禁止直接序列化到磁盤/網絡。標記 transient 後,反序列化為 null,業務層重新加密賦值。
InvalidClassExceptionprivate static final long serialVersionUID = 1L;private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject(); // 默認序列化非 transient 字段
out.writeObject(encrypt(password)); // 敏感字段加密後寫入
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
this.password = decrypt((String) in.readObject()); // 讀取解密
}
public class Data implements Externalizable {
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(id);
out.writeUTF(name);
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.id = in.readInt();
this.name = in.readUTF();
}
}
需實現 writeExternal/readExternal,不再自動序列化任何字段,靈活度最高但編碼量大。
ObjectInputFilter 白名單過濾| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| Java 原生序列化 | 零配置、支援對象圖、RMI 必需 | 體積大、速度慢、不跨語言、安全風險高 | 內部 RPC、舊系統兼容 |
| JSON (Jackson/Gson) | 可讀、跨語言、生態成熟、安全 | 無類型信息、對象圖需配置、循環引用需處理 | Web API、配置文件、日誌 |
| Protobuf / Avro | 體積極小、速度極快、Schema 演進、跨語言 | 需定義 .proto/.avsc、學習成本、不可直接讀 | 高性能微服務、大數據傳輸、Kafka |
| Hessian / Kryo | 二進制、較快、支援對象圖、Java 生態 | 跨語言弱、Kryo 需註冊類 | Dubbo 默認、緩存存儲 |
Java 序列化雖然內建便捷,但體積大、速度慢、跨語言差、安全風險高。現代架構中:對外介面用 JSON/Protobuf,內部高性能用 Protobuf/Kryo,僅在 RMI/舊系統兼容時使用原生序列化。若必須用原生,請務必:serialVersionUID 顯式聲明、敏感字段 transient、啟用 ObjectInputFilter、定期掃描依賴漏洞。下一篇我們將深入 JVM 類加載機制與雙親委派模型。