| 類/介面 | 關鍵方法 | 用途 |
|---|---|---|
ObjectOutputStream | writeObject(Object) | 序列化物件寫入輸出流 |
ObjectInputStream | readObject() | 從輸入流讀取並反序列化物件 |
Serializable | 標記介面 (無方法) | 啟用預設序列化機制 |
Externalizable | writeExternal/readExternal | 完全自定義序列化邏輯 |
import java.io.*;
import java.util.Date;
public class SerializationDemo {
public static void main(String[] args) throws Exception {
// 序列化
try (ObjectOutputStream out = new ObjectOutputStream(
new FileOutputStream("objectFile.obj"))) {
Customer customer = new Customer("阿蜜果", 24);
out.writeObject("你好!");
out.writeObject(new Date());
out.writeObject(customer);
out.writeInt(123);
}
// 反序列化 (順序必須一致)
try (ObjectInputStream in = new ObjectInputStream(
new FileInputStream("objectFile.obj"))) {
System.out.println("obj1=" + (String) in.readObject());
System.out.println("obj2=" + (Date) in.readObject());
Customer obj3 = (Customer) in.readObject();
System.out.println("obj3=" + obj3);
int obj4 = in.readInt();
System.out.println("obj4=" + obj4);
}
}
}
class Customer implements Serializable {
private static final long serialVersionUID = 1L; // 強烈建議顯式宣告
private String name;
private int age;
public Customer(String name, int age) { this.name = name; this.age = age; }
@Override public String toString() { return "name=" + name + ", age=" + age; }
}
標記欄位 不參與預設序列化,反序列化時該欄位為預設值 (null/0/false)。適用於敏感資料 (密碼)、衍生欄位、非序列化第三方物件引用。
在類中定義 private 方法,JVM 會自動調用:
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());
}
靜態屬於類不屬於實例,預設機制忽略。若需持久化需手動在 writeObject 中處理。
Externalizable 必須實作 writeExternal(ObjectOutput)、readExternal(ObjectInput)。public 無參建構子,否則拋 InvalidClassException。InvalidClassException。| 策略 | UID 設定 | 適用場景 |
|---|---|---|
| 兼容升級 | 固定同一值 (如 1L) | 新增欄位、方法、實現新介面;舊資料仍可反序列化,新欄位得預設值 |
| 強制不兼容 | 修改 UID (如 2L) | 欄位刪除/型別變更/語義改變,舊資料不應被讀取 |
// 兼容升級範例
class CustomerV2 implements Serializable {
private static final long serialVersionUID = 1L; // 保持不變
private String name;
private int age;
private String email; // 新增欄位,舊資料反序列化時 email = null
// ...
}
transient 減少傳輸體積 (大集合、快取、日誌物件等)。Externalizable 或改用高效二進制協議 (Protobuf, Avro, Kryo, Hessian)。反序列化不可信資料極度危險 (CVE-2015-4852 等 Gadget Chain 攻擊)。生產環境務必:
- 禁止直接反序列化用戶輸入的位元組流。
- 使用
ObjectInputFilter(JDK 9+) 白名單限制可反序列化類別。- 採用安全協議 (JSON/Protobuf) 替代 Java 原生序列化進行跨系統通訊。
Java 序列化雖然內建便利,但預設機制脆弱、效能一般、版本演進易出錯、安全風險高。現代架構建議:
ObjectInputFilter、定期審計、最小權限原則。