Java 默認序列化雖然簡便,但涉及 transient 處理、效能陷阱、版本相容性等進階主題,開發者必須深入理解才能避坑。
標記 transient 的欄位將跳過序列化/反序列化,但反序列化後會獲得類型默認值(null/0/false)。開發者需自行在 readObject 中恢復合理初始值:
public class Serial implements Serializable {
int companyId;
String companyAddr;
transient boolean companyFlag; // 不序列化
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject();
companyFlag = false; // 必須顯式初始化
}
}
默認序列化採用物件圖遍歷,遇到雙向關聯、循環引用(如雙向鏈表、樹結構)時:
StackOverflowError解決方案:自定義 writeObject/readObject,僅序列化邏輯所需的核心資料結構。
JVM 通過 serialVersionUID(隱式或顯式)判斷類版本是否匹配。三種相容策略:
private static final long serialVersionUID = 123456789L; // 人為指定新值
// 舊版本反序列化拋出 InvalidClassException
serialVersionUID 不變private static final long serialVersionUID = 762508508425139227L;
private final long version = 5552; // 業務版本號,非 transient
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject();
switch ((int) version) {
case 5552: initV5552Fields(); break;
case 5550: initV5550Fields(); break;
default: throw new InvalidClassException("Unsupported version");
}
validateConstraints(); // 資料一致性校驗
}
| 機制 | 適用場景 | 注意事項 |
|---|---|---|
readObject 校驗 + 防禦性拷貝 | 一般類、需繼承的類 | 需逐欄位校驗,子類易忘記調用父類初始化 |
readResolve 返回新實例 | final 類、單例、高安全需求 | 父類實現會導致子類反序列化得到父類實例,慎用於可繼承類 |