在 Java 序列化機制中,transient 關鍵字扮演著「守門人」角色:標記字段不參與序列化。本文深入解析其原理、適用場景、注意事項與實戰模式。
實現 Serializable 接口的類,默認所有非 static、非 transient 字段會被自動序列化。加上 transient 後:
null,基本類型 0/false)| 場景 | 示例字段 | 理由 |
|---|---|---|
| 敏感信息 | password、token、bankCard、idNumber | 安全合規,防止數據落盤/傳輸洩露 |
| 派生/計算字段 | fullName(由 firstName+lastName 組合)、totalPrice(由 items 計算) | 可重新計算,無需持久化,節省空間 |
| 非序列化資源引用 | Thread、Connection、Socket、Logger、Stream | 這些對象不實現 Serializable,強制序列化會拋異常 |
| 緩存/臨時狀態 | cache、tempBuffer、computedHash | 運行時重建即可,無需持久化 |
import java.io.*;
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String username;
private transient String password; // 不序列化
private transient Connection dbConn; // 資源引用,不可序列化
private String fullName; // 可序列化,或用 transient + 反序列化後重建
// 反序列化後自動調用,重建 transient 字段
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
// 重新建立數據庫連接、計算派生字段等
this.dbConn = DriverManager.getConnection(URL, USER, PWD);
this.fullName = this.username + "_restored";
}
// 序列化前調用,可做敏感數據脫敏
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
// 可在此加密 password 後寫入單獨字段(但通常直接 transient 更簡潔)
}
}
若需對 transient 字段進行加密存儲或特殊編碼,可實現 private void writeObject(ObjectOutputStream) 與 private void readObject(ObjectInputStream),手動控制寫入/讀取邏輯,而非完全放棄持久化。
static 屬於類,不屬於對象,transient static 無意義但合法。readObject 使用 Unsafe 或反射設置,不推薦。transient 字段同樣為默認值。serialVersionUID,否則類結構變更導致反序列化失敗(InvalidClassException)。| 特性 | transient + Serializable | Externalizable |
|---|---|---|
| 控制粒度 | 字段級(排除) | 對象級(完全手動) |
| 實現複雜度 | 低(默認機制+少量排除) | 高(需實現 writeExternal/readExternal) |
| 性能 | 一般(反射) | 高(直接流操作) |
| 適用場景 | 大多數業務對象 | 高性能/特殊格式/大對象 |
@JsonIgnore、Gson transient 同樣生效,更通用、跨語言。transient 是 Java 原生序列化中的「精準屏蔽器」,用於敏感數據保護、資源引用隔離、派生字段優化。掌握其與 static、final、readObject/writeObject 的交互細節,能避免反序列化後的「空指針驚魂」與數據洩露風險。新項目優先考慮 JSON/二進制協議替代原生序列化。