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

Java transient 關鍵字實戰筆記:序列化排除、static 陷阱與 Externalizable 繞過機制

     編輯:Java 相關知識

前言:為什麼要認真理解 transient?

在實作物件持久化、網路傳輸、分散式快取時,Java 序列化機制雖然方便(實作 Serializable 即自動生效),但往往伴隨著「某些欄位不想被序列化」的需求。transient 正是為此而生,然而許多開發者僅知皮毛,對 static 交互行為、Externalizable 繞過機制一知半解。本文透過三個可直接執行的完整案例,徹底釐清 transient 的真實行為邊界。


案例一:基礎驗證——transient 讓密碼欄位「消失」

程式碼

import java.io.*; public class TransientBasicTest { public static void main(String[] args) throws Exception { User user = new User(); user.setUsername("Alexia"); user.setPasswd("123456"); System.out.println("序列化前:username=" + user.getUsername() + ", password=" + user.getPasswd()); // 序列化 ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("user.dat")); os.writeObject(user); os.close(); // 反序列化 ObjectInputStream is = new ObjectInputStream(new FileInputStream("user.dat")); user = (User) is.readObject(); is.close(); System.out.println("序列化後:username=" + user.getUsername() + ", password=" + user.getPasswd()); } } class User implements Serializable { private static final long serialVersionUID = 1L; private String username; private transient String passwd; // 關鍵:標記 transient public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPasswd() { return passwd; } public void setPasswd(String passwd) { this.passwd = passwd; } }

輸出結果

序列化前:username=Alexia, password=123456 序列化後:username=Alexia, password=null

結論

transient 欄位在反序列化後獲得預設值(null),不會從位元流還原。


案例二:static 變量的「假象」與真相

現象

若將 username 改為 static,反序列化後似乎仍能讀到值,導致誤以為 static 會被序列化。

驗證程式碼(關鍵:反序列化前修改 static 值)

public class StaticTransientDeepTest { public static void main(String[] args) throws Exception { User user = new User(); user.setUsername("Alexia"); user.setPasswd("123456"); // 序列化 ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("user.dat")); os.writeObject(user); os.close(); // ★關鍵:反序列化前修改靜態變量 User.username = "jmwang"; // 反序列化 ObjectInputStream is = new ObjectInputStream(new FileInputStream("user.dat")); user = (User) is.readObject(); is.close(); System.out.println("username=" + user.getUsername()); // 輸出 jmwang(當前 JVM 值) System.out.println("password=" + user.getPasswd()); // 輸出 null } } class User implements Serializable { public static String username; // static 不序列化 private transient String passwd; // getter/setter 省略 }

輸出結果

username=jmwang password=null

鐵律總結

static 變量屬於類,不屬於實例,永遠不會被 Java 原生序列化機制序列化。 反序列化後讀到的值,是當前 JVM 中該類的靜態欄位當前值,而非序列化時刻的值。transient 對 static 無任何額外影響。


案例三:Externalizable 讓 transient 完全失效

原理

實作 Externalizable 介面表示「我要完全手動控制序列化格式」,JVM 不再自動處理任何欄位,transient 自然失效。開發者在 writeExternal 中決定寫什麼、readExternal 中決定讀什麼。

程式碼驗證

import java.io.*; public class ExternalizableBypassTest implements Externalizable { private transient String content = "我被 transient 標記,但會被序列化!"; @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(content); // 手動寫入,無視 transient } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { content = (String) in.readObject(); } public static void main(String[] args) throws Exception { ExternalizableBypassTest obj = new ExternalizableBypassTest(); System.out.println("序列化前:" + obj.content); ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("ext.dat")); os.writeObject(obj); os.close(); ObjectInputStream is = new ObjectInputStream(new FileInputStream("ext.dat")); obj = (ExternalizableBypassTest) is.readObject(); is.close(); System.out.println("序列化後:" + obj.content); // 輸出原字串,證明 transient 失效 } }

輸出結果

序列化前:我被 transient 標記,但會被序列化! 序列化後:我被 transient 標記,但會被序列化!

核心知識點速查表

場景是否序列化反序列化後值備註
一般實例變量是原值需實作 Serializable
transient 實例變量否預設值 (null/0/false)標準序列化有效
static 變量否當前 JVM 類靜態值與 transient 無關
static + transient否當前 JVM 類靜態值transient 無額外效果
Externalizable + transient由開發者決定由開發者決定transient 完全失效

實務建議

  1. 敏感資料(密碼、Token)務必加 transient,並考慮改用 JSON 序列化(Jackson/Gson)配合 @JsonIgnore,更直觀且跨語言。
  2. 避免在可序列化類中使用 static 保存實例狀態,極易造成誤解。
  3. 若需精細控制二進制格式、版本相容、效能,改用 Externalizable 或 Protobuf/Kryo 等高效序列化框架。
  4. 永遠宣告 private static final long serialVersionUID,防止類變更導致反序列化失敗。

作者:Alexia (minmin)|原文連結:Java transient 關鍵字使用小記

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved