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

深度解析 Java 序列化機制:原理、實踐與進階場景

     編輯:Java 相關知識

什麼是 Java 序列化?

Java 序列化是將對象的狀態(數據、類型信息等)轉換為字節序列的過程,以便在網絡傳輸、文件持久化或進程間通信中使用。反序列化則是將字節序列還原為對象的逆過程。核心類包括 ObjectOutputStream(寫入/序列化)和 ObjectInputStream(讀取/反序列化)。

基礎序列化實戰

實現 Serializable 標記接口是序列化的前提:

public class Employee implements Serializable {
    private int employeeId;
    private String employeeName;
    private String department;
    // getter/setter 省略
}

序列化操作:

// 序列化
try (FileOutputStream fos = new FileOutputStream("employee.ser");
     ObjectOutputStream oos = new ObjectOutputStream(fos)) {
    Employee emp = new Employee();
    emp.setEmployeeId(101);
    emp.setEmployeeName("Arpit");
    emp.setDepartment("CS");
    oos.writeObject(emp);
}

// 反序列化
try (FileInputStream fis = new FileInputStream("employee.ser");
     ObjectInputStream ois = new ObjectInputStream(fis)) {
    Employee emp = (Employee) ois.readObject();
    System.out.println("Emp id: " + emp.getEmployeeId());
}

關鍵概念:serialVersionUID

serialVersionUID 是類的版本標識,用於反序列化時驗證發送方和接收方的類版本一致。建議顯式聲明:

private static final long serialVersionUID = 1L;

若未聲明,JVM 會根據類結構自動生成,類結構變更會導致 InvalidClassException。

進階場景深度剖析

場景一:對象引用鏈的自動序列化

序列化對象時,其引用的所有對象會自動被序列化(對象圖)。但所有被引用的類必須實現 Serializable,否則拋出 NotSerializableException。

場景二:無法修改引用類源碼時

若引用類不可序列化且無法修改源碼:

  • 方案 A:將字段標記為 transient 跳過序列化(反序列化後為 null)
  • 方案 B:自定義 writeObject/readObject 手動處理字段讀寫

自定義序列化示例:

private transient Address address; // 跳過默認序列化

private void writeObject(ObjectOutputStream os) throws IOException {
    os.defaultWriteObject(); // 先寫入非 transient 字段
    os.writeInt(address.getHomeNo()); // 手動寫入 Address 字段
    os.writeObject(address.getStreet());
    os.writeObject(address.getCity());
}

private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException {
    is.defaultReadObject(); // 先讀取非 transient 字段
    int homeNo = is.readInt(); // 按寫入順序讀取
    String street = (String) is.readObject();
    String city = (String) is.readObject();
    address = new Address(homeNo, street, city);
}

關鍵點:讀取順序必須與寫入順序完全一致。

場景三:繼承體系中的序列化

父類狀態子類序列化行為
父類實現 Serializable子類自動可序列化,父類字段正常序列化
父類未實現 Serializable反序列化時調用父類無參構造器初始化父類字段,子類字段正常還原
父類可序列化,但子類不想序列化子類實現 writeObject/readObject 並拋出 NotSerializableException

序列化核心規則總結

  • 靜態字段(static)不參與序列化,屬於類級別而非對象級別
  • transient 字段被跳過,反序列化後為默認值(對象為 null)
  • 構造器不會在反序列化時調用(除非父類不可序列化)
  • 單例模式需配合 readResolve() 防止反序列化破壞單例
  • 跨平台特性:JVM 獨立性允許在不同平台間序列化/反序列化

最佳實踐建議

  1. 顯式聲明 serialVersionUID
  2. 謹慎使用 transient,敏感數據(密碼等)必須標記
  3. 優先考慮 JSON/Protobuf 等現代序列化方案替代 Java 原生序列化
  4. 反序列化不可信數據存在安全風險,需嚴格校驗
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved