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

Java 序列化進階:父類未實作 Serializable 時的子類序列化策略

     編輯:J2SE

問題背景

當父類未實作 Serializable 介面,但子類需要序列化時,預設機制會失效。Java 文件規定:子類必須負責序列化/反序列化父類的非靜態、非暫態欄位,且父類必須提供無參建構子。

錯誤示範與編譯錯誤

public class SuperC { // 未實作 Serializable
    int supervalue;
    public SuperC(int v) { this.supervalue = v; }
    // 缺少無參建構子
}

public class SubC extends SuperC implements Serializable {
    int subvalue;
    // 直接序列化會拋出 InvalidClassException:
    // "no valid constructor"
}

正確實作模式

public abstract class SuperC {
    int supervalue;
    public SuperC(int v) { this.supervalue = v; }
    public SuperC() {} // 必須提供無參建構子
}

public class SubC extends SuperC implements Serializable {
    int subvalue;
    public SubC(int sv, int v) { super(sv); this.subvalue = v; }

    // 自訂序列化邏輯
    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject(); // 先序列化自身欄位
        out.writeInt(supervalue); // 再序列化父類欄位
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();   // 先反序列化自身欄位
        supervalue = in.readInt(); // 再反序列化父類欄位
    }
}

核心原理解析

  • defaultWriteObject / defaultReadObject:委派給預設序列化機制處理當前類別的非暫態欄位
  • 欄位順序一致性:寫入與讀取父類欄位的順序、型別必須完全一致
  • 存取權限:父類欄位需為 public、protected 或同套件 package-private,否則子類無法直接存取
  • serialVersionUID:建議顯式宣告,避免類別變更導致反序列化失敗

進階情境:多層繼承鏈

若繼承鏈中有多個非序列化父類,每一層都需在對應子類中手動處理。建議在最底層可序列化的類別集中管理所有祖先欄位的序列化邏輯,或使用外部化 Externalizable 完全自控。

替代方案比較

方案優點缺點
自訂 writeObject/readObject相容預設機制,改動最小需手動維護欄位順序
實作 Externalizable完全控制序列化格式需手寫所有欄位邏輯,繁瑣
使用序列化代理模式封裝複雜邏輯,保護不變條件增加額外類別
改用 JSON/Protobuf 等跨語言、版本相容性佳非 Java 原生,需額外庫

最佳實踐總結

  1. 優先讓父類實作 Serializable,若不可行(如第三方庫)再用子類代理
  2. 父類務必提供 public/protected 無參建構子
  3. 序列化父類欄位時,嚴格對應 write/read 呼叫順序與型別
  4. 單元測試覆蓋序列化/反序列化全流程,包含版本演進場景
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved