transient仅对java原生序列化生效,需类实现serializable、字段为非static非final实例变量;对json、日志、数据库等无效,应结合@jsonignore、dto隔离、char[]存储等多层防护。

直接在字段声明前加上 transient 关键字即可跳过 Java 原生序列化(ObjectOutputStream)对该字段的处理。但它只对 java.io.Serializable 有效,不防日志、JSON、数据库或调试器泄露。
基础写法与生效条件
必须满足三个前提:类实现 Serializable 接口;字段是实例变量(不能是 static 或 final);使用的是 JDK 原生序列化流程。
- 正确示例:
private transient String password; -
static字段本身不参与序列化,加transient是冗余的 -
final字段若在构造时已初始化,仍可能被序列化;加transient虽能编译通过,但语义混乱,不推荐 - 反序列化后,该字段值为默认值:引用类型为
null,int为0,boolean为false
为什么加了 transient 还出现在 JSON 响应里
因为 Jackson、Gson 等主流 JSON 库默认完全忽略 transient —— 它们不走 Java 原生序列化路径,而是基于反射+注解解析字段。
- Jackson:需显式启用支持:
mapper.configure(MapperFeature.USE_TRANSIENT_ANNOTATION, true);更稳妥的是直接用@JsonIgnore - Gson:默认也不识别,可用
GsonBuilder.excludeFieldsWithModifiers(Modifier.TRANSIENT) - Spring Boot 默认用 Jackson,所以加
transient对@ResponseBody返回的 JSON 没作用
常见失效场景与应对建议
transient 不是安全开关,它只屏蔽一条路径(原生序列化),其他泄露渠道照常发生。
- 日志打印:
toString()仍会输出password值 → 重写toString(),跳过敏感字段 - 内存 dump 或调试器查看 →
transient完全无效 → 敏感字段改用char[],用完立即清空 - 自定义
writeObject()方法中手动写入该字段 → 会被重新序列化 → 检查并避免在该方法中操作transient字段 - JPA/Hibernate、MyBatis、HTTP 请求体(
@RequestBody)→ 它们有自己的映射逻辑,transient无影响 → 敏感字段不应出现在实体类或 DTO 中,改用专用响应对象
更可靠的替代方案
现代开发中,仅靠 transient 已无法满足数据防护要求,应分层控制暴露面:
- DTO 隔离:定义
UserResponse类,只含前端需要的字段,从不声明password - 注解优先:Jackson 用
@JsonIgnore,Fastjson 用@JSONField(serialize = false),Gson 用@Expose(serialize = false) - 构建时检查:CI 流程中用 SpotBugs 的
SE_BAD_FIELD规则,扫描Serializable类中未标记transient的敏感字段 - 运行时清理:对密码类字段,用
char[]存储,业务逻辑结束后调用Arrays.fill(pwd, '\u0000')










