transient 仅阻止字段参与 java 原生序列化,反序列化时设为默认值;它不防日志、调试或内存泄露,对 json、jpa 等无效,且可被 writeobject() 绕过。

transient 关键字在序列化中到底起什么作用
transient 是 Java 中用于标记字段的修饰符,它只在对象序列化(如 ObjectOutputStream)过程中生效:被标记的字段不会写入字节流,反序列化时该字段值为默认值(null、0、false 等)。它不提供加密、访问控制或运行时保护,仅影响序列化行为本身。
常见误解是以为加了 transient 就“安全了”——其实只要对象还在 JVM 堆里,toString()、日志打印、调试器、反射都能直接读到它的值。它只防序列化路径,不防内存泄露。
哪些场景下用 transient 才真正有效
它只在明确走标准 Java 序列化流程时起作用,比如:
- 使用
ObjectOutputStream/ObjectInputStream传输对象(如老式 RMI) - 将对象存入基于
Serializable的缓存(如某些版本的 Ehcache 默认序列化存储) - Spring Session 使用
DefaultCookieSerializer且未禁用序列化时(已不推荐)
对 JSON(ObjectMapper)、数据库 ORM(JPA、MyBatis)、HTTP 请求体(@RequestBody)等完全无效——这些框架有自己的序列化逻辑,不看 transient。
为什么 password 字段加了 transient 还出现在日志里
因为日志通常来自 toString()、Lombok 的 @ToString、或手动拼接字符串,和序列化无关。例如:
public class User implements Serializable {
private String username;
private transient String password; // ← 对日志无影响
public String toString() {
return "User{username='" + username + "', password='" + password + "'}"; // ← 这里仍会打印 password
}
}
解决方式不是依赖 transient,而是:
- 重写
toString(),显式跳过敏感字段 - 用 Lombok 时加
@ToString(exclude = "password") - 日志中避免直接打印整个对象,改用结构化字段输出
- 敏感字段类型改用
char[]并及时Arrays.fill(pwd, '\u0000')清理
替代 transient 的更可靠做法
现代系统中,transient 已基本被更明确、可审计的机制取代:
- JSON 序列化:用
@JsonIgnore(Jackson)或@Expose(serialize = false)(Gson) - JPA 实体:用
@Transient(注意大小写!这是 JPA 注解,和transient关键字无关) - DTO 设计:敏感字段根本不出现在 DTO 类中,靠构造函数或 Builder 控制字段暴露
- 配置项校验:CI 阶段用 SpotBugs 规则
SE_BAD_FIELD检查Serializable类中是否有未标记transient的敏感字段
真正容易被忽略的是:transient 不阻止子类继承后重新序列化该字段,也不影响 writeObject() 方法里手动写出它——只要重写了这个方法,就绕过了所有 transient 限制。










