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

Java 對象序列化深度探索:機制原理、transient 控制與 serialVersionUID 陷阱

     編輯:Java 相關知識

核心概念

  • 序列化:對象 → 字節流(存儲/傳輸)
  • 反序列化:字節流 → 對象(恢復狀態)
  • 標記接口:Serializable 無方法,通知 JVM 該類可序列化
  • 自定義控制:實現 Externalizable 完全接管讀寫邏輯

標準流程代碼示例

// 序列化
Person person = new Person("熔巖", "341022225562156", "lavasoft");
try (ObjectOutputStream oos = new ObjectOutputStream(
        new FileOutputStream("person.dat"))) {
    oos.writeObject(person);
}

// 反序列化
Person person;
try (ObjectInputStream ois = new ObjectInputStream(
        new FileInputStream("person.dat"))) {
    person = (Person) ois.readObject();
}
System.out.println(person); // password 為 null

transient 關鍵字:敏感字段保護

class Person implements Serializable {
    private String username;
    private String cardNumber;
    private transient String passWord; // 不參與序列化
    // getter/setter/toString
}

反序列化後 passWord 為 null(引用類型默認值),實現密碼等敏感信息不落盤。

serialVersionUID:版本控制的生命線

問題背景

反序列化時,JVM 比對流中 serialVersionUID 與當前類的 UID。不一致拋出 InvalidClassException。

UID 來源優先級

  1. 顯式聲明:private static final long serialVersionUID = 42L;(強烈推薦)
  2. 隱式計算:基於類結構(字段、方法、接口等)自動生成,極其敏感,跨編譯器/版本極易不一致

最佳實踐

  • 必須顯式聲明,且使用 private(繼承無意義)
  • 初始版本可設為 1L,兼容性修改不變更,破壞性修改(如刪除字段、改變類型)必須遞增
  • IDE 生成:Eclipse 右鍵 Source → Generate serialVersion ID;IntelliJ 檢查提示自動生成

序列化進階機制

1. 自定義序列化(部分控制)

private void writeObject(ObjectOutputStream out) throws IOException {
    out.defaultWriteObject(); // 先執行默認邏輯
    out.writeObject(socket.getInetAddress()); // 手寫 transient 字段
    out.writeInt(socket.getPort());
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    in.defaultReadObject();
    InetAddress addr = (InetAddress) in.readObject();
    int port = in.readInt();
    this.socket = new Socket(addr, port); // 重建 transient 對象
}

關鍵:讀寫順序必須完全一致;調用 defaultWriteObject/ReadObject 保留自動處理非 transient 字段。

2. Externalizable:完全接管

public class FullControl implements Externalizable {
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(field1);
        out.writeInt(field2);
    }
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        field1 = in.readObject();
        field2 = in.readInt();
    }
}

風險:方法為 public,惡意代碼可調用竊取/篡改數據。敏感對象需加密流或安全管理器保護。

避坑指南

坑點後果對策
未聲明 serialVersionUID跨環境反序列化失敗顯式聲明 private static final long
transient 字段反序列化後為 null/0業務邏輯 NPE 或錯誤readObject 中重建或賦默認值
父類未實現 Serializable父類字段不序列化,反序列化調用無參構造器父類也實現 Serializable 或子類手動處理
集合元素非 Serializable序列化拋出 NotSerializableException確保對象圖全鏈路可序列化
單例模式破壞反序列化產生新實例實現 readResolve() 返回單例實例

總結

Java 序列化雖簡單強大,但 serialVersionUID、transient 重建、readResolve、安全性等細節決定生產可用性。原則:**顯式 UID、最小化序列化面、敏感數據加密、單例保護、充分測試跨版本兼容**。

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