transient 关键字使字段跳过 java 原生序列化,jvm 直接不写入该字段,反序列化后为默认值;它不影响 json、orm 等其他序列化机制,且不继承,需显式声明。

transient 关键字为什么能让字段跳过序列化
Java 默认序列化机制(ObjectOutputStream)会把对象所有非静态、非瞬态字段的值写入字节流。加了 transient 的字段,会被 JVM 明确标记为“不参与序列化”,序列化时直接跳过——不是清空、不是默认值替代,是彻底不写入。
常见错误现象:NotSerializableException 报在某个字段上,但你确认该字段类型明明可序列化;其实是因为它被嵌在另一个 transient 字段引用的对象里,而那个外层字段没标 transient,导致整个引用链被尝试序列化。
-
transient只影响 Java 原生序列化(ObjectOutputStream/ObjectInputStream),对 JSON(如 Jackson)、数据库 ORM(如 Hibernate)无效——它们各自有一套排除逻辑 - 静态字段天然不序列化,加不加
transient都一样;但加上更清晰,避免误以为它会被序列化 - 反序列化后,
transient字段值为对应类型的默认值(null、0、false),不会调用构造函数或初始化块重设
哪些字段必须标 transient
凡是运行时生成、与当前 JVM 实例强绑定、或敏感不可落盘的数据,都该加 transient。
典型使用场景:缓存计算结果、线程局部变量、数据库连接、日志器、加密密钥、HTTP 会话 ID 等。
立即学习“Java免费学习笔记(深入)”;
- 比如一个
private Logger logger字段:Log4j/JUL 的Logger类通常没实现Serializable,不加transient直接报错 - 比如
private transient int cachedHashCode:这个值只在内存中有效,序列化后再反序列化,哈希码应该重新计算,不该保留旧值 - 比如
private transient ThreadLocal<string> context</string>:ThreadLocal 本身不可序列化,且其内容只对当前线程有意义
transient 和自定义序列化 writeObject/readObject 冲突吗
不冲突,但顺序和意图容易搞混。如果你写了 private void writeObject(ObjectOutputStream out),JVM 就完全跳过默认序列化逻辑——包括对 transient 字段的自动跳过行为。
这意味着:你在 writeObject 里手动 out.defaultWriteObject(),才触发 transient 的默认跳过;如果漏掉这句,或者自己用 out.writeInt() 等写所有字段,那 transient 就失效了。
- 想部分控制 + 保留
transient行为?必须在writeObject开头调用out.defaultWriteObject() - 想彻底接管,并显式写出某个
transient字段?可以,但得自己处理反序列化时的恢复逻辑,否则该字段永远为默认值 - Jackson 等库的
@JsonIgnore或@JsonInclude(JsonInclude.Include.NON_DEFAULT)是另一套机制,和transient无关,别混用
序列化框架(如 Jackson)认 transient 吗
Jackson 默认不认 transient,它只看字段可见性(public 字段或 getter/setter)和注解。但可以开启兼容模式:objectMapper.configure(SerializationFeature.WRITE_TRANSIENT, true),此时它会跳过 transient 字段。
不过更常见、更可控的做法是用注解:@JsonIgnore(完全忽略)、@JsonProperty(access = JsonProperty.Access.READ_ONLY)(只序列化不反序列化)。
- Hibernate/JPA 不管
transient,它靠@Transient注解(注意大小写)来排除字段映射到数据库 - Android 的 Parcelable 也不认
transient,得靠writeToParcel()里主动跳过字段 - 哪怕你用的是 Java 原生序列化,也要小心:如果父类字段没标
transient,子类继承后也没覆盖,那它依然会被序列化——transient不继承
真正容易被忽略的是:序列化上下文切换。同一个类,在 RPC 调用走 Hessian、前端交互走 JSON、本地缓存走 Java 序列化——每种路径的“排除规则”都不同,transient 只保 Java 原生那一程。










