本系列深度解讀《編寫高質量代碼:改善 Java 程序的 151 個建議》(秦小波著),每篇覆蓋 5 條建議,結合源碼級分析與實戰經驗,助你內化為肌肉記憶。
serialVersionUID 校驗類版本一致性。InvalidClassException。private static final long serialVersionUID = 1867341609628930239L;
顯式聲明 UID 可避免不一致,但切勿濫用掩蓋不兼容變更(如刪除關鍵字段、修改字段類型)。嚴重不兼容應升級 UID 強制失效,暴露問題而非隱藏問題。
public class Person implements Serializable {
public final String name;
public Person() { name = "程咬金"; } // 構造函數賦值
}
序列化後修改構造函數賦值為 "秦叔寶",反序列化結果仍為 "程咬金"。
// 推薦:聲明處直接賦值或靜態工廠方法
public final String name = "程咬金";
// 或
public final String name = initName();
private static String initName() { return "程咬金"; }
public final String name = initName();,修改方法返回值後反序列化不生效,原因同建議 12:反序列化不執行實例初始化代碼(包括字段初始化表達式)。
public class Person implements Serializable {
private transient Salary salary; // transient 阻止默認序列化
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject(); // 序列化非 transient 字段
out.writeInt(salary.getBasePay()); // 手動寫入基本工資
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject(); // 反序列化非 transient 字段
int basePay = in.readInt(); // 手動讀取基本工資
this.salary = new Salary(basePay, 0); // 績效工資置 0
}
}
private void writeObject(ObjectOutputStream) 和 private void readObject(ObjectInputStream),JVM 反射檢測到後會委托這兩個方法替代默認序列化/反序列化邏輯。defaultWriteObject()/defaultReadObject() 處理標準字段,手動代碼處理特殊字段。switch (n) {
case 0: chinese = "零";
case 1: chinese = "壹"; // 漏寫 break,貫穿執行
...
}
輸入 0 輸出 "玖"。金融系統中文大寫轉換錯誤直接導致資金損失。
'switch' case fall-through 設置為 Error 級別,漏寫直接紅叉報錯。| 建議 | 核心知識點 | 實戰原則 |
|---|---|---|
| 11 | serialVersionUID 版本控制 | 必須顯式聲明,嚴禁濫用掩蓋不兼容 |
| 12 | 反序列化跳過構造函數 | final 字段聲明處直接賦值,不用構造函數 |
| 13 | 反序列化跳過實例初始化 | final 字段避免方法調用/複雜表達式賦值 |
| 14 | 私有 writeObject/readObject 委托機制 | 敏感字段用 transient + 手動序列化控制 |
| 15 | switch 貫穿特性 | IDE 設為 Error 級別,養成必寫 break 習慣 |
下期預告:建議 16-20,涵蓋 equals/hashCode 契約、Cloneable 陷阱、Comparable 實現規範等核心基礎話題。