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

Java transient 關鍵字深度解析:序列化控制、實戰場景與 HashMap 源碼案例

     編輯:Java 相關知識

transient 是 Java 序列化機制中的「隱形開關」,用於標記字段不參與序列化。本文從序列化原理切入,深度解析 transient 作用機制、適用場景、實戰示例,並以 HashMap 源碼 modCount 為例說明核心類庫的設計智慧。

前置知識:Java 序列化機制

  • 目的:將對象圖轉為字節序列,用於網絡傳輸(RPC)、持久化(緩存落盤)、進程間通訊。
  • 契約:實現 java.io.Serializable 標記接口(無方法)。
  • 流程:ObjectOutputStream.writeObject() → 字節流 → ObjectInputStream.readObject() → 恢復對象。
  • 反序列化不調用構造器:直接分配內存、按字節填充字段,transient 字段將得到默認值(null/0/false)。

transient 核心作用

被 transient 修飾的實例字段,序列化時被忽略,反序列化後恢復為類型默認值。

class User implements Serializable {
    private String username;
    private transient String password;      // 敏感信息不落盤/不傳輸
    private transient Logger logger = LoggerFactory.getLogger(User.class); // 非串行化資源
    private transient int age;              // 衍生字段:可從 birthDate 計算
}

三大核心適用場景

實戰完整示例:矩形類

class Rectangle implements Serializable {
    private static final long serialVersionUID = 1L;
    private int width;
    private int height;
    private transient int area; // 衍生字段

    public Rectangle(int w, int h) {
        this.width = w; this.height = h; this.area = w * h;
    }
    public void recalcArea() { this.area = width * height; }
    @Override public String toString() {
        return "w=" + width + ", h=" + height + ", area=" + area;
    }
}

public class TransientDemo {
    public static void main(String[] args) throws Exception {
        Rectangle r = new Rectangle(3, 4);
        System.out.println("原始: " + r); // area=12

        // 序列化
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("rect.ser"))) {
            oos.writeObject(r);
        }

        // 反序列化
        Rectangle r2;
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("rect.ser"))) {
            r2 = (Rectangle) ois.readObject();
        }
        System.out.println("反序列化: " + r2); // area=0 (默認值)

        // 恢復衍生字段
        r2.recalcArea();
        System.out.println("恢復後: " + r2); // area=12
    }
}

源碼級案例:HashMap.modCount

// HashMap.java (JDK 8)
/**
 * The number of times this HashMap has been structurally modified.
 * Used to make iterators on Collection-views fail-fast.
 */
transient int modCount;

設計考量:

  1. modCount 記錄結構性修改次數(put/remove/rehash),用於 Iterator 的 fail-fast 機制。
  2. 反序列化後,modCount 歸零完全正確:新對象尚未發生任何修改,迭代器應基於新狀態計數。
  3. 若序列化 modCount,反序列化繼承舊值會導致:
    ① 新插入元素時 modCount 不連續,
    ② 並發場景下可能誤觸發 ConcurrentModificationException。

這體現了「序列化只保存業務狀態,不保存運行時輔助狀態」的核心原則。

進階:自定義序列化控制(writeObject/readObject)

若需對 transient 字段進行特殊處理(如加密密碼、重建連接),可實現私有方法:

private void writeObject(ObjectOutputStream out) throws IOException {
    out.defaultWriteObject();
    out.writeObject(encrypt(password)); // 加密存儲
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    in.defaultReadObject();
    this.password = decrypt((String) in.readObject()); // 解密恢復
}

常見誤區

  • static 字段不受 transient 影響:static 屬於類,本身不參與對象序列化(除非實現 Externalizable)。
  • final 字段:若為基本類型/String 常量表達式,編譯期常量折疊會直接內聯,transient 無效;引用類型 final 字段若 transient,反序列化後為 null(需在 readObject 中賦值)。
  • 父類非 Serializable:子類實現 Serializable,反序列化會調用父類無參構造器初始化父類字段,transient 僅影響當前類聲明的字段。

總結

transient 是序列化的「精準手術刀」:敏感數據不出域、運行時資源不持久化、衍生字段不佔空間。配合 serialVersionUID、自定義 writeObject/readObject,構建安全、高效、可演進的序列化方案。閱讀 JDK 集合源碼(HashMap、ArrayList、ConcurrentHashMap)中的 transient 用法,是提升架構設計能力的捷徑。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
場景示例理由
敏感數據密碼、Token、銀行卡號防止序列化後在磁盤/網絡明文洩露
非串行化資源Logger、Thread、Connection、Socket、鎖這些資源無意義/無法跨進程恢復,強行序列化會報錯
衍生/可計算字段面積=長×寬、全名=姓+名、緩存的 hashCode節省空間,反序列化後重新計算即可