JAVA transient 關(guān)鍵字作用詳解
一、transient關(guān)鍵字作用
- 作用:
transient用于修飾成員變量,表示該變量不參與序列化過程。 - 場景:當(dāng)對象被序列化(如寫入磁盤、通過網(wǎng)絡(luò)傳輸)時,
transient修飾的變量不會被保存到序列化流中。
二、原理詳解
Java 的對象序列化機制(如 ObjectOutputStream)會將對象的所有非靜態(tài)、非瞬態(tài)(transient)成員變量序列化到字節(jié)流中。transient 關(guān)鍵字告訴 JVM:該變量是臨時的,不需要序列化。
反序列化時,transient 變量會被賦予默認(rèn)值(如 int 為 0,引用類型為 null)。
三、典型使用場景
- 敏感信息
- 如密碼、身份證號等,不希望被序列化存儲或傳輸。
- 臨時計算結(jié)果
- 如緩存、臨時狀態(tài),不需要持久化。
- 不可序列化對象
- 某些成員變量類型沒有實現(xiàn) Serializable,可以用 transient 跳過。
四、代碼示例
import java.io.*;
class User implements Serializable {
private String username;
private transient String password; // 不序列化
public User(String username, String password) {
this.username = username;
this.password = password;
}
}
public class TransientDemo {
public static void main(String[] args) throws Exception {
User user = new User("Tom", "123456");
// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.obj"));
oos.writeObject(user);
oos.close();
// 反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.obj"));
User user2 = (User) ois.readObject();
ois.close();
System.out.println(user2.username); // Tom
System.out.println(user2.password); // null
}
}五、注意事項
- 只修飾變量,不能修飾方法、類、局部變量。
- 靜態(tài)變量不參與序列化,即使沒有 transient 修飾,也不會被序列化。
- 自定義序列化:如果實現(xiàn)了
writeObject和readObject,可以手動控制 transient 變量的序列化行為。
六、與 static 的區(qū)別
static修飾的變量屬于類,不屬于對象,不參與序列化。transient修飾的變量屬于對象,但被標(biāo)記為不參與序列化。
七、面試常見問題
- transient 變量能否被序列化?
- 默認(rèn)不會被序列化,但可以通過自定義序列化方法手動序列化。
- 序列化后 transient 變量的值是什么?
- 反序列化后為默認(rèn)值(int 為 0,引用類型為 null)。
- transient 和 static 有什么區(qū)別?
- static 不參與序列化,transient 標(biāo)記為不參與序列化。
- 為什么要用 transient?
- 保護(hù)敏感信息、優(yōu)化存儲、避免不可序列化成員導(dǎo)致序列化失敗。
八、補充:自定義序列化 transient 變量
如果你想讓 transient 變量也能序列化,可以自定義 writeObject/readObject:
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
oos.writeObject(password); // 手動序列化
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
password = (String) ois.readObject(); // 手動反序列化
}九、總結(jié)表
| 特性 | 說明 |
|---|---|
| 作用 | 標(biāo)記變量不參與序列化 |
| 默認(rèn)值 | 反序列化后為默認(rèn)值 |
| 修飾對象 | 只能修飾成員變量 |
| 典型場景 | 敏感信息/臨時變量/不可序列化對象 |
| 可否自定義 | 可通過 writeObject/readObject 手動處理 |
十. 序列化機制與 transient 的底層原理
Java 的序列化機制通過 ObjectOutputStream 和 ObjectInputStream 實現(xiàn)。序列化時,JVM 會遍歷對象的所有字段,只有滿足以下條件的字段才會被序列化:
- 非 static
- 非 transient
- 類型實現(xiàn)了
Serializable接口
transient 的本質(zhì):
JVM 在序列化對象時,會判斷字段是否被 transient 修飾,如果是,則跳過該字段,不寫入字節(jié)流。
十一. 常見誤區(qū)與陷阱
誤區(qū)一:transient 修飾的變量絕對不會被序列化
實際情況:如果你自定義了 writeObject/readObject 方法,可以手動序列化 transient 字段。
誤區(qū)二:transient 只能用于敏感信息
實際情況:transient 還可以用于臨時計算、緩存、不可序列化成員等場景。
誤區(qū)三:static transient 有意義
實際情況:static 字段本身就不會被序列化,transient 修飾 static 字段沒有任何作用。
十二. 進(jìn)階應(yīng)用:自定義序列化 transient 字段
有時你希望 transient 字段能被序列化,但不想默認(rèn)序列化,可以自定義序列化方法:
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject(); // 序列化非transient字段
oos.writeObject(this.transientField); // 手動序列化transient字段
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject(); // 反序列化非transient字段
this.transientField = (Type) ois.readObject(); // 手動反序列化transient字段
}十三. 實際應(yīng)用場景擴(kuò)展
- 分布式系統(tǒng):如 RPC 框架、緩存對象,常用 transient 跳過網(wǎng)絡(luò)不需要傳輸?shù)谋镜貭顟B(tài)。
- 安全與合規(guī):如日志審計、敏感數(shù)據(jù)脫敏,防止敏感信息被持久化或泄露。
- 性能優(yōu)化:減少序列化內(nèi)容,提升網(wǎng)絡(luò)傳輸和磁盤存儲效率。
- 不可序列化對象:如數(shù)據(jù)庫連接、線程池等,這些對象不能序列化,否則會報錯。
十四. 面試陷阱代碼分析
陷阱一:
class Test implements Serializable {
private transient int a = 10;
private int b = 20;
}如果序列化后反序列化,a 的值是多少?
答案: 0(int 默認(rèn)值),b 是 20。
陷阱二:
class Test implements Serializable {
private static transient int a = 10;
}序列化后反序列化,a 的值是多少?
答案: 10(static 字段不會被序列化,值取決于類加載時的靜態(tài)初始化)
十五. transient 與其他關(guān)鍵字的對比
| 關(guān)鍵字 | 作用 | 是否序列化 |
|---|---|---|
| transient | 標(biāo)記不序列化成員變量 | 否 |
| static | 類變量,不屬于對象 | 否 |
| final | 常量,只能賦值一次 | 是 |
十六. 進(jìn)階:transient 與可變對象
如果 transient 修飾的是引用類型變量(如 List),反序列化后會變?yōu)?null。
如果需要恢復(fù)數(shù)據(jù),可以在 readObject 方法里初始化或重建。
十七. 典型面試題總結(jié)
- transient 修飾的變量序列化后會是什么?
- 對于基本類型,是默認(rèn)值;對于引用類型,是 null。
- transient 可以修飾 static 嗎?
- 可以,但沒有實際意義。
- transient 變量能否通過自定義序列化保存?
- 可以,需實現(xiàn) writeObject/readObject 方法。
- 為什么要用 transient?
- 安全、性能、避免不可序列化成員導(dǎo)致異常。
十八. 補充:序列化版本號與 transient
serialVersionUID是 static final 字段,不參與序列化。- 如果類結(jié)構(gòu)變化,反序列化可能失敗,transient 字段不會影響序列化兼容性。
十九.transient與序列化機制的深層關(guān)系
1 序列化的過程
- Java 的序列化機制會遞歸地序列化對象的每個非 static、非 transient 字段。
- 如果字段類型本身不可序列化(沒有實現(xiàn)
Serializable),但未被 transient 修飾,序列化會拋出NotSerializableException。
2 反序列化過程
- 反序列化時,transient 字段不會從流中恢復(fù),直接賦予默認(rèn)值。
- 如果你希望反序列化后給 transient 字段賦初值,可以用如下方式:
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
this.transientField = ...; // 這里可以初始化
}二十.transient相關(guān)的安全問題
1 敏感數(shù)據(jù)泄露
- 如果敏感字段未被 transient 修飾,序列化后可能被寫入磁盤或通過網(wǎng)絡(luò)傳輸,造成安全隱患。
- 建議:對所有敏感信息(如密碼、token、身份證號等)都加上 transient。
2 反序列化漏洞
- transient 字段不會參與序列化和反序列化,但如果你在 readObject 里對 transient 字段做了不安全的初始化,也可能被攻擊者利用(如反序列化 gadget 鏈)。
二十一. 性能優(yōu)化相關(guān)
- 對于大對象,如果某些字段無需持久化,使用 transient 可以顯著減少序列化數(shù)據(jù)體積,提高網(wǎng)絡(luò)傳輸和存儲性能。
- 在高性能緩存、分布式對象同步等場景,合理使用 transient 能避免不必要的負(fù)擔(dān)。
二十二. 在主流框架中的應(yīng)用
1 Spring
- Spring 的 Bean、Session 等對象有時會被序列化傳遞,常用 transient 跳過如數(shù)據(jù)庫連接、線程池、日志等不可序列化資源。
2 Hibernate/JPA
- 實體類中有些字段(如 logger、臨時緩存)會加 transient,避免持久化和序列化。
3 分布式緩存/消息隊列
- Redis、Kafka、RocketMQ 等都會用到對象序列化,transient 可以跳過本地狀態(tài)和敏感字段。
二十三. 進(jìn)階:transient 與自定義序列化框架
- Java原生序列化只認(rèn) transient,第三方序列化(如 Jackson、Fastjson、Kryo、Protobuf)不一定認(rèn) transient。
- 比如 Jackson 需要配合 @JsonIgnore 注解,而不是 transient。
- 工程建議:如果需要跨多種序列化框架,建議同時用 transient 和注解(如 @JsonIgnore)雙重標(biāo)記。
二十四. 面試高頻“陷阱”與實戰(zhàn)建議
1 面試陷阱代碼
class Person implements Serializable {
private transient int age = 18;
private String name = "Tom";
}序列化后反序列化,age 是多少?
答案:0(int 默認(rèn)值),不是 18。
2 實戰(zhàn)建議
- 對所有不可序列化或敏感字段,都加 transient。
- 如果需要恢復(fù) transient 字段值,建議在 readObject 里初始化。
- 對于跨多種序列化框架的項目,建議加上相應(yīng)的注解。
二十五. 結(jié)合實際業(yè)務(wù)場景舉例
1 緩存對象
class CacheObject implements Serializable {
private String data;
private transient long lastAccessTime; // 僅本地有效
}2 日志字段
class ServiceBean implements Serializable {
private String id;
private transient Logger log = LoggerFactory.getLogger(ServiceBean.class);
}3 線程池、數(shù)據(jù)庫連接等資源
class Job implements Serializable {
private transient ExecutorService threadPool;
private transient Connection dbConn;
}二十六. 小結(jié)
transient是 Java 原生序列化機制的關(guān)鍵字,主要用于跳過不需要序列化的字段。- 適用于敏感信息、臨時狀態(tài)、不可序列化成員、性能優(yōu)化等多種場景。
- 結(jié)合自定義序列化,可以靈活控制序列化和反序列化行為。
- 在分布式、緩存、框架開發(fā)等實際工程中非常常見。
- transient 用于控制序列化行為,保護(hù)敏感信息,優(yōu)化性能,處理不可序列化成員。
- 反序列化后 transient 字段為默認(rèn)值。
- 可通過自定義序列化方法手動保存/恢復(fù) transient 字段。
到此這篇關(guān)于JAVA transient 關(guān)鍵字詳解的文章就介紹到這了,更多相關(guān)java transient 關(guān)鍵字內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java調(diào)用ChatGPT(基于SpringBoot和Vue)實現(xiàn)可連續(xù)對話和流式輸出的ChatGPT API
這篇文章主要介紹了Java調(diào)用ChatGPT(基于SpringBoot和Vue),實現(xiàn)可連續(xù)對話和流式輸出的ChatGPT API(可自定義實現(xiàn)AI助手),文中代碼示例介紹的非常詳細(xì),感興趣的朋友可以參考下2023-04-04
Spring?Cloud?中使用?Sentinel?實現(xiàn)服務(wù)限流的兩種方式
這篇文章主要介紹了Spring?Cloud?中使用?Sentinel?實現(xiàn)服務(wù)限流的方式,通過示例代碼主要介紹了Sentinel的兩種實現(xiàn)限流的方式,需要的朋友可以參考下2024-03-03

