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

Java transient 與 volatile 混淆辨析:一篇搞懂兩大關鍵字區別

     編輯:Java 綜合教學

transient vs volatile:同為關鍵字,功能天壤之別

網上不少資料將 transient 與 volatile 混淆,甚至錯誤標註。本文釐清兩者核心差異,並深入解析 transient 在序列化中的完整行為。

核心區別一覽表

維度transientvolatile
核心領域對象序列化/持久化多線程內存可見性
作用目標實例字段實例/靜態字段
效果字段不參與序列化,反序列化為默認值強制從主內存讀寫,禁止指令重排序
典型場景敏感數據、派生字段、非序列化對象引用狀態標誌、雙重檢查鎖、單例模式

volatile 關鍵字回顧

volatile 保證:

  1. 可見性:寫入立即刷新到主內存,讀取強制從主內存獲取
  2. 有序性:禁止指令重排序(JMM happens-before 規則)
  3. 不保證原子性:i++ 等複合操作仍需同步或原子類
// 典型用法:線程停止標誌
volatile boolean running = true;
void stop() { running = false; }
void run() { while (running) { /* ... */ } }

transient 完整行為解析

1. 基本規則

  • 修飾實例變量,static/final 字段雖可加但無實際效果(static 不屬於實例,final 序列化由編譯器處理)
  • 反序列化時:引用類型 → null,基本類型 → 0/false
  • 構造函數不會被調用,對象通過「分配內存 + 填充字段」直接恢復

2. 陷阱案例:帶默認值的 transient 字段

class GuestLoggingInfo implements Serializable {
    private String uid = "guest";
    private transient String pwd = "guest"; // 有默認值

    public GuestLoggingInfo() {} // 無參構造函數

    public String toString() {
        String password = (pwd == null) ? "NOT SET" : pwd;
        return "user: " + uid + "\npassword: " + password;
    }
}

// 測試
GuestLoggingInfo g = new GuestLoggingInfo();
System.out.println("序列化前: " + g); // password: guest

// 序列化 + 反序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("guest.ser"));
oos.writeObject(g);
oos.close();

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("guest.ser"));
GuestLoggingInfo g2 = (GuestLoggingInfo) ois.readObject();
System.out.println("反序列化後: " + g2); // password: NOT SET ← 構造函數未執行!

關鍵點:反序列化不調用構造函數,直接分配內存並填充非 transient 字段,transient 字段保持默認值(null),而非構造函數賦予的默認值。

3. 進階用法:自定義序列化恢復 transient 字段

class SecureData implements Serializable {
    private transient String decryptedCache; // 解密後緩存,不序列化
    private String encryptedData; // 加密存儲

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject(); // 默認序列化非 transient 字段
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject(); // 默認反序列化
        // 此處可觸發解密邏輯重建 decryptedCache
        this.decryptedCache = decrypt(encryptedData);
    }
}

總結

  • volatile 解決並發可見性,是多線程編程利器
  • transient 解決序列化選擇性,是持久化/傳輸安全衛士
  • 兩者無任何關聯,切勿混淆
  • 掌握 transient 「不調用構造函數」特性,避免反序列化後字段狀態不符預期
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved