transient 是 Java 中的一个字段修饰符,主要用于对象序列化(Serialization)时控制某些字段不被序列化

transient 的中文翻译是 "瞬态的" 或 "临时的"。

下面从概念、原理、使用场景、示例和注意事项进行系统讲解。


一、什么是 transient

当一个类实现了 Serializable 接口后,对象就可以被序列化(转换为字节流保存或传输)。
如果某个字段使用 transient 修饰,那么:

该字段不会被序列化,也不会被反序列化恢复。

反序列化后,该字段会变成默认值:

  • 引用类型:null

  • 数值类型:0

  • boolean:false


二、为什么需要 transient

常见原因:

1. 安全性(最常见)

例如密码不希望被序列化:

class User implements Serializable {
    private String username;
    private transient String password;
}

序列化后,password 不会写入文件或网络。


2. 字段可以重新计算

例如缓存字段:

class Order implements Serializable {
    private double price;
    private int count;
    private transient double totalPrice;
}

totalPrice 可以通过
price * count 重新计算。


3. 不可序列化对象

某些对象本身不支持序列化,例如:

private transient Thread thread;
private transient Connection connection;

三、基本示例

示例代码

import java.io.*;

class Person implements Serializable {
    String name;
    transient int age;

    Person(String name, int age){
        this.name = name;
        this.age = age;
    }
}

public class Test {
    public static void main(String[] args) throws Exception {

        Person p = new Person("Tom", 20);

        // 序列化
        ObjectOutputStream oos =
                new ObjectOutputStream(new FileOutputStream("data.txt"));
        oos.writeObject(p);
        oos.close();

        // 反序列化
        ObjectInputStream ois =
                new ObjectInputStream(new FileInputStream("data.txt"));
        Person p2 = (Person) ois.readObject();
        ois.close();

        System.out.println(p2.name); // Tom
        System.out.println(p2.age);  // 0
    }
}

输出:

Tom
0

因为 agetransient 修饰。


四、transient 的底层原理

Java 序列化核心类:

ObjectOutputStream
ObjectInputStream

序列化时,JVM 会:

  1. 通过反射获取字段

  2. 跳过 transient 修饰的字段

  3. 写入字节流

即:

transient = 不参与序列化

五、transient + 自定义序列化(高级)

即使字段是 transient,仍然可以通过自定义方法手动序列化。

private void writeObject(ObjectOutputStream out) throws Exception {
    out.defaultWriteObject();
    out.writeInt(age);   // 手动写入
}

private void readObject(ObjectInputStream in) throws Exception {
    in.defaultReadObject();
    age = in.readInt();  // 手动恢复
}

这在加密场景很常见。


六、transient 和 static 的区别

很多人容易混淆。

修饰符

是否序列化

原因

transient

不序列化

显式忽略

static

不序列化

属于类,不属于对象

示例:

static int a;
transient int b;

两者都不会被序列化,但原因不同。


七、常见面试考点(高频)

1. transient 能修饰哪些?

只能修饰:

成员变量

不能修饰:

方法
类
局部变量

2. transient 修饰的变量反序列化后一定是 null 吗?

不一定:

  • 基本类型 → 默认值

  • 引用类型 → null

  • 如果自定义 readObject → 可以恢复


3. transient 和 final 一起使用?

可以,但要注意:

transient final int a = 10;

反序列化后仍然是 10,因为是常量。


八、真实开发中的典型场景

场景1:密码字段

private transient String password;

场景2:缓存字段

private transient Map cache;

场景3:数据库连接

private transient Connection conn;

九、容易踩坑的点(重要)

Jackson / Fastjson 不完全遵守 transient

例如 JSON 序列化:

  • Jackson 默认忽略 transient(可配置)

  • Fastjson 有时不会忽略

如果是接口返回,推荐使用:

@JsonIgnore

而不是仅依赖 transient。


十、一句话总结

transient 的本质作用:让字段不参与 Java 序列化。