JAVA transient 关键字作用详解

 更新时间:2025年11月28日 11:44:05   作者:猩火燎猿  
Java的transient关键字用于修饰成员变量,使其不参与序列化过程,通过自定义序列化方法,可以手动控制transient变量的序列化行为,本文给大家介绍JAVA transient 关键字作用,感兴趣的朋友跟随小编一起看看吧

一、transient关键字作用

  • 作用transient 用于修饰成员变量,表示该变量不参与序列化过程。
  • 场景:当对象被序列化(如写入磁盘、通过网络传输)时,transient 修饰的变量不会被保存到序列化流中。

二、原理详解

Java 的对象序列化机制(如 ObjectOutputStream)会将对象的所有非静态、非瞬态(transient)成员变量序列化到字节流中。
transient 关键字告诉 JVM:该变量是临时的,不需要序列化。

反序列化时transient 变量会被赋予默认值(如 int 为 0,引用类型为 null)。

三、典型使用场景

  • 敏感信息
  • 如密码、身份证号等,不希望被序列化存储或传输。
  • 临时计算结果
  • 如缓存、临时状态,不需要持久化。
  • 不可序列化对象
  • 某些成员变量类型没有实现 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
    }
}

五、注意事项

  1. 只修饰变量,不能修饰方法、类、局部变量。
  2. 静态变量不参与序列化,即使没有 transient 修饰,也不会被序列化。
  3. 自定义序列化:如果实现了 writeObject 和 readObject,可以手动控制 transient 变量的序列化行为。

六、与 static 的区别

  • static 修饰的变量属于类,不属于对象,不参与序列化。
  • transient 修饰的变量属于对象,但被标记为不参与序列化。

七、面试常见问题

  • transient 变量能否被序列化?
    • 默认不会被序列化,但可以通过自定义序列化方法手动序列化。
  • 序列化后 transient 变量的值是什么?
    • 反序列化后为默认值(int 为 0,引用类型为 null)。
  • transient 和 static 有什么区别?
    • static 不参与序列化,transient 标记为不参与序列化。
  • 为什么要用 transient?
    • 保护敏感信息、优化存储、避免不可序列化成员导致序列化失败。

八、补充:自定义序列化 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(); // 手动反序列化
}

九、总结表

特性说明
作用标记变量不参与序列化
默认值反序列化后为默认值
修饰对象只能修饰成员变量
典型场景敏感信息/临时变量/不可序列化对象
可否自定义可通过 writeObject/readObject 手动处理

十. 序列化机制与 transient 的底层原理

Java 的序列化机制通过 ObjectOutputStream 和 ObjectInputStream 实现。序列化时,JVM 会遍历对象的所有字段,只有满足以下条件的字段才会被序列化:

  • 非 static
  • 非 transient
  • 类型实现了 Serializable 接口

transient 的本质
JVM 在序列化对象时,会判断字段是否被 transient 修饰,如果是,则跳过该字段,不写入字节流。

十一. 常见误区与陷阱

误区一:transient 修饰的变量绝对不会被序列化

实际情况:如果你自定义了 writeObject/readObject 方法,可以手动序列化 transient 字段。

误区二:transient 只能用于敏感信息

实际情况:transient 还可以用于临时计算、缓存、不可序列化成员等场景。

误区三:static transient 有意义

实际情况:static 字段本身就不会被序列化,transient 修饰 static 字段没有任何作用。

十二. 进阶应用:自定义序列化 transient 字段

有时你希望 transient 字段能被序列化,但不想默认序列化,可以自定义序列化方法:

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字段
}

十三. 实际应用场景扩展

  • 分布式系统:如 RPC 框架、缓存对象,常用 transient 跳过网络不需要传输的本地状态。
  • 安全与合规:如日志审计、敏感数据脱敏,防止敏感信息被持久化或泄露。
  • 性能优化:减少序列化内容,提升网络传输和磁盘存储效率。
  • 不可序列化对象:如数据库连接、线程池等,这些对象不能序列化,否则会报错。

十四. 面试陷阱代码分析

陷阱一:

class Test implements Serializable {
    private transient int a = 10;
    private int b = 20;
}

如果序列化后反序列化,a 的值是多少?
答案: 0(int 默认值),b 是 20。

陷阱二:

class Test implements Serializable {
    private static transient int a = 10;
}

序列化后反序列化,a 的值是多少?
答案: 10(static 字段不会被序列化,值取决于类加载时的静态初始化)

十五. transient 与其他关键字的对比

关键字作用是否序列化
transient标记不序列化成员变量
static类变量,不属于对象
final常量,只能赋值一次

十六. 进阶:transient 与可变对象

如果 transient 修饰的是引用类型变量(如 List),反序列化后会变为 null。
如果需要恢复数据,可以在 readObject 方法里初始化或重建。

十七. 典型面试题总结

  • transient 修饰的变量序列化后会是什么?
    • 对于基本类型,是默认值;对于引用类型,是 null。
  • transient 可以修饰 static 吗?
    • 可以,但没有实际意义。
  • transient 变量能否通过自定义序列化保存?
    • 可以,需实现 writeObject/readObject 方法。
  • 为什么要用 transient?
    • 安全、性能、避免不可序列化成员导致异常。

十八. 补充:序列化版本号与 transient

  • serialVersionUID 是 static final 字段,不参与序列化。
  • 如果类结构变化,反序列化可能失败,transient 字段不会影响序列化兼容性。

十九.transient与序列化机制的深层关系

1 序列化的过程

  • Java 的序列化机制会递归地序列化对象的每个非 static、非 transient 字段。
  • 如果字段类型本身不可序列化(没有实现 Serializable),但未被 transient 修饰,序列化会抛出 NotSerializableException

2 反序列化过程

  • 反序列化时,transient 字段不会从流中恢复,直接赋予默认值。
  • 如果你希望反序列化后给 transient 字段赋初值,可以用如下方式:
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
    ois.defaultReadObject();
    this.transientField = ...; // 这里可以初始化
}

二十.transient相关的安全问题

1 敏感数据泄露

  • 如果敏感字段未被 transient 修饰,序列化后可能被写入磁盘或通过网络传输,造成安全隐患。
  • 建议:对所有敏感信息(如密码、token、身份证号等)都加上 transient。

2 反序列化漏洞

  • transient 字段不会参与序列化和反序列化,但如果你在 readObject 里对 transient 字段做了不安全的初始化,也可能被攻击者利用(如反序列化 gadget 链)。

二十一. 性能优化相关

  • 对于大对象,如果某些字段无需持久化,使用 transient 可以显著减少序列化数据体积,提高网络传输和存储性能。
  • 在高性能缓存、分布式对象同步等场景,合理使用 transient 能避免不必要的负担。

二十二. 在主流框架中的应用

1 Spring

  • Spring 的 Bean、Session 等对象有时会被序列化传递,常用 transient 跳过如数据库连接、线程池、日志等不可序列化资源。

2 Hibernate/JPA

  • 实体类中有些字段(如 logger、临时缓存)会加 transient,避免持久化和序列化。

3 分布式缓存/消息队列

  • Redis、Kafka、RocketMQ 等都会用到对象序列化,transient 可以跳过本地状态和敏感字段。

二十三. 进阶:transient 与自定义序列化框架

  • Java原生序列化只认 transient,第三方序列化(如 Jackson、Fastjson、Kryo、Protobuf)不一定认 transient。
  • 比如 Jackson 需要配合 @JsonIgnore 注解,而不是 transient。
  • 工程建议:如果需要跨多种序列化框架,建议同时用 transient 和注解(如 @JsonIgnore)双重标记。

二十四. 面试高频“陷阱”与实战建议

1 面试陷阱代码

class Person implements Serializable {
    private transient int age = 18;
    private String name = "Tom";
}

序列化后反序列化,age 是多少?
答案:0(int 默认值),不是 18。

2 实战建议

  • 对所有不可序列化或敏感字段,都加 transient。
  • 如果需要恢复 transient 字段值,建议在 readObject 里初始化。
  • 对于跨多种序列化框架的项目,建议加上相应的注解。

二十五. 结合实际业务场景举例

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 线程池、数据库连接等资源

class Job implements Serializable {
    private transient ExecutorService threadPool;
    private transient Connection dbConn;
}

二十六. 小结

  • transient 是 Java 原生序列化机制的关键字,主要用于跳过不需要序列化的字段。
  • 适用于敏感信息、临时状态、不可序列化成员、性能优化等多种场景。
  • 结合自定义序列化,可以灵活控制序列化和反序列化行为。
  • 在分布式、缓存、框架开发等实际工程中非常常见。
  • transient 用于控制序列化行为,保护敏感信息,优化性能,处理不可序列化成员。
  • 反序列化后 transient 字段为默认值。
  • 可通过自定义序列化方法手动保存/恢复 transient 字段。

到此这篇关于JAVA transient 关键字详解的文章就介绍到这了,更多相关java transient 关键字内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

最新评论