Java 序列化是將對象狀態轉換為位元組流,以便持久化存儲或網絡傳輸的核心機制。本文深入剖析 Serializable 接口的工作原理、完整流程、代碼實戰及常被忽視的安全與資源風險。
1. 安全風險:敏感數據(如密碼、加密金鑰、身份證號)序列化後以位元組流形式存在,極易被反序列化還原竊取,破壞數據機密性。
2. 資源耗盡風險:反序列化可無限制創建對象實例,惡意構造的序列化數據可能導致大量對象湧入堆內存,引發 OOM 攻擊。
最佳實踐:敏感字段標記transient;實現Externalizable自定義加密邏輯;反序列化前驗證數據來源合法性。
java.io.Serializable 是一個**標記接口**,無任何方法。它向 JVM 聲明:「該類同意被默認序列化機制處理」。實現該接口的類,其對象即可被 ObjectOutputStream 寫出、ObjectInputStream 讀入。
FileOutputStream → ObjectOutputStreamwriteObject(obj) 觸發遞歸寫入對象圖Person p = new Person("Alice", "123456");
File file = new File("cache.dat");
try (FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos)) {
oos.writeObject(p);
}
FileInputStream → ObjectInputStreamreadObject() 讀取位元組流重建對象圖try (FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis)) {
Person p = (Person) ois.readObject();
System.out.println(p.getName() + "---" + p.getId());
}
JVM 使用 serialVersionUID 驗證發送端與接收端類版本一致性。
private static final long serialVersionUID = 1L;(強烈推薦,防止跨 JVM 部署版本不一致導致 InvalidClassException)標記字段不參與默認序列化,反序列化後該字段為默認值(null/0/false)。常用於敏感數據、非持久化緩存、計算衍生字段。
實現以下私有方法(非接口方法,由反射調用)完全掌控流程:
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject(); // 執行默認邏輯
// 額外邏輯:加密敏感字段、寫入額外元數據
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
// 額外邏輯:解密、校驗、初始化 transient 字段
}
繼承 Serializable,強制實現 writeExternal/readExternal,**完全放棄默認機制**,適合高性能、加密、壓縮等深度定制場景。
Serializable,子類自動可序列化;父類未實現,子類序列化時父類字段需有無參構造函數初始化。Serializable。transient 字段不參與序列化。Java 默認序列化雖便利,但**性能一般、跨語言差、安全風險高**。現代架構中,REST API 多採用 JSON (Jackson/Gson)、Protobuf、Avro 等高效二進制協議替代。若必須使用原生序列化,請務必:顯式聲明 serialVersionUID、敏感字段 transient、反序列化入口加驗證、考慮 Externalizable 替代。