當父類未實作 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(); // 再反序列化父類欄位
}
}
public、protected 或同套件 package-private,否則子類無法直接存取serialVersionUID:建議顯式宣告,避免類別變更導致反序列化失敗若繼承鏈中有多個非序列化父類,每一層都需在對應子類中手動處理。建議在最底層可序列化的類別集中管理所有祖先欄位的序列化邏輯,或使用外部化 Externalizable 完全自控。
| 方案 | 優點 | 缺點 |
|---|---|---|
| 自訂 writeObject/readObject | 相容預設機制,改動最小 | 需手動維護欄位順序 |
| 實作 Externalizable | 完全控制序列化格式 | 需手寫所有欄位邏輯,繁瑣 |
| 使用序列化代理模式 | 封裝複雜邏輯,保護不變條件 | 增加額外類別 |
| 改用 JSON/Protobuf 等 | 跨語言、版本相容性佳 | 非 Java 原生,需額外庫 |
Serializable,若不可行(如第三方庫)再用子類代理public/protected 無參建構子write/read 呼叫順序與型別