首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 序列化機制深度解析:從入門到實戰

     編輯:Java 相關知識

什麼是 Java 序列化?

序列化將物件狀態轉換為位元組串流,便於持久化儲存(檔案、資料庫)或網路傳輸(RMI、HTTP)。反序列化則是將位元組串流重建為物件。Java 透過 Serializable 標記介面與 ObjectOutputStream/ObjectInputStream 實現此機制,設計極為簡潔。

核心 API 與介面

類別/介面角色關鍵方法
Serializable標記介面,無方法—
ObjectOutputStream序列化輸出writeObject(Object)
ObjectInputStream反序列化輸入readObject()
Externalizable自訂序列化協議writeExternal/readExternal

基本實作範例

可序列化類別定義

public class Serial implements Serializable {
    private static final long serialVersionUID = 1L;  // 強烈建議顯式宣告
    
    int companyId;
    String companyAddr;
    boolean companyFlag;
    
    // 建構子、getter/setter、toString()...
}

序列化與反序列化操作

// 序列化寫入檔案
try (FileOutputStream fos = new FileOutputStream("data.ser");
     ObjectOutputStream oos = new ObjectOutputStream(fos)) {
    
    Serial obj = new Serial(752, "Dayer Street #5", false);
    oos.writeObject(obj);
}

// 反序列化讀取檔案
try (FileInputStream fis = new FileInputStream("data.ser");
     ObjectInputStream ois = new ObjectInputStream(fis)) {
    
    Serial obj = (Serial) ois.readObject();
    System.out.println(obj);  // DATA: 752 Dayer Street #5 false
}

進階特性與陷阱

1. serialVersionUID 版本控制

  • 未宣告時 JVM 自動依據類別結構產生,修改類別會導致 InvalidClassException
  • 顯式宣告 private static final long serialVersionUID = 1L; 可控制相容性
  • 相容性變更:增欄位(相容)、減欄位(相容)、改型別(不相容)

2. transient 關鍵字

private transient String password;  // 不序列化敏感資料
private transient Logger logger;    // 非可序列化物件

反序列化時 transient 欄位預設為 null/0/false,需在 readObject() 中手動恢復。

3. 自訂序列化邏輯

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());
}

4. 單例模式與序列化

序列化會破壞單例(每次反序列化產生新實例)。解決:實作 readResolve() 回傳單例實例。

private Object readResolve() {
    return INSTANCE;  // 回傳唯一實例
}

效能考量與最佳實務

序列化效能優化

  • 實作 Externalizable 完全掌控二進位格式
  • 使用 ByteArrayOutputStream 記憶體操作避免磁碟 I/O
  • 大量物件考慮 ObjectOutputStream.reset() 避免記憶體洩漏
  • 高效能場景改用 Protobuf、Avro、Kryo、MessagePack

安全性風險

反序列化不受信資料可導致遠端程式碼執行(RCE)。防禦措施:

  • JDK 9+ 使用 ObjectInputFilter 白名單類別
  • 避免反序列化未知來源資料
  • 採用 JSON/Protobuf 替代 Java 原生序列化

實戰應用場景

  1. Session 持久化:Web 容器將 Session 物件序列化至磁碟/資料庫實現容錯
  2. RMI 遠端呼叫:參數與回傳值自動序列化傳輸
  3. 分散式快取:Redis/Memcached 儲存序列化物件
  4. 訊息佇列:JMS/Kafka 訊息本體序列化
  5. 深度複製:serialize -> deserialize 實現物件圖完整複製

總結

Java 序列化雖然簡單易用,但效能一般、版本演進複雜、安全風險高。現代架構建議:內部通訊用 Protobuf/Avro,對外 API 用 JSON,僅在相容舊系統時使用原生序列化。掌握 serialVersionUID、transient、自訂 readObject/writeObject、readResolve 等進階機制,才能駕馭複雜序列化場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved