1. 简介

本文将深入探讨 Java 中的 transient 关键字,并通过实际示例演示其在不同数据类型中的行为表现。

2. transient 的使用场景

在讲解 transient 之前,我们先简单回顾下序列化机制。

序列化(Serialization)是将对象转换为字节流的过程,反序列化(Deserialization)则是其逆过程。

当我们对某个字段使用 transient 关键字修饰时,该字段将不会参与序列化过程。由于该字段在序列化后的字节流中并不存在,因此在反序列化时会使用其默认值(如 null0)来初始化。

transient 主要用于以下几种情况:

  • 派生字段(即可以通过其他字段计算得出的字段)
  • 不代表对象状态的字段
  • 无法序列化的引用类型字段
  • 存储敏感信息,不想在网络中传输的字段 ❌

3. 示例演示

为了更直观地理解 transient 的作用,我们创建一个 Book 类用于序列化操作:

public class Book implements Serializable {
    private static final long serialVersionUID = -2936687026040726549L;
    private String bookName;
    private transient String description;
    private transient int copies;
    
    // getters and setters
}

在上面的类中,我们将 descriptioncopies 字段标记为 transient

接着创建一个 Book 实例并赋值:

Book book = new Book();
book.setBookName("Java Reference");
book.setDescription("will not be saved");
book.setCopies(25);

然后将该对象序列化到文件中:

public static void serialize(Book book) throws Exception {
    FileOutputStream file = new FileOutputStream(fileName);
    ObjectOutputStream out = new ObjectOutputStream(file);
    out.writeObject(book);
    out.close();
    file.close();
}

再从文件中反序列化出对象:

public static Book deserialize() throws Exception {
    FileInputStream file = new FileInputStream(fileName);
    ObjectInputStream in = new ObjectInputStream(file);
    Book book = (Book) in.readObject();
    in.close();
    file.close();
    return book;
}

最后验证字段值:

assertEquals("Java Reference", book.getBookName());
assertNull(book.getDescription());
assertEquals(0, book.getCopies());

可以看到,bookName 字段被正确保存和恢复,而 descriptioncopies 字段则被设置为其默认值(分别为 null0),而非原始值。

4. transient 与 final 的结合使用

4.1. 字面量初始化的 final 字段

我们先在 Book 类中添加一个 final transient 字段:

public class Book implements Serializable {
    // existing fields    
    
    private final transient String bookCategory = "Fiction";

    // getters and setters
}

创建一个空对象:

Book book = new Book();

final 字段在声明时直接赋值(字面量初始化),即使加上 transient,也不会影响其值的恢复。

因为这种情况下,该字段的值在编译期就已经确定,并存储在类的常量池中。由于是 final,其值不可更改,所以在反序列化时会从类定义中获取值,而不是 null

assertEquals("Fiction", book.getBookCategory());

⚠️ 更多关于字符串常量池的内容可以参考文章 Guide to Java String Pool

4.2. 使用 new 操作符初始化的 final 字符串

如果使用 new 操作符初始化 final transient 字段:

public class Book implements Serializable {
    // existing fields    
    
    private final transient String bookCategoryNewOperator = new String("Fiction with new Operator");

    // getters and setters
}

创建对象:

Book book = new Book();

此时,字符串对象是在堆内存中创建的,因此在反序列化时其值将为 null

assertNull(book.getBookCategoryNewOperator());

5. 总结

本文通过多个示例详细讲解了 transient 关键字在 Java 序列化中的行为表现,包括其与 final 结合使用时的特殊情况。

虽然 transient 是一个看似简单的关键字,但在实际使用中,特别是在涉及 final、字符串池等机制时,仍有一些“坑”需要注意。

✅ 所有示例代码均可在 GitHub 仓库中获取:GitHub - core-java-modules/core-java-lang-3


原始标题:The transient Keyword in Java