1. 简介
本文将深入探讨 Java 中的 transient
关键字,并通过实际示例演示其在不同数据类型中的行为表现。
2. transient 的使用场景
在讲解 transient
之前,我们先简单回顾下序列化机制。
✅ 序列化(Serialization)是将对象转换为字节流的过程,反序列化(Deserialization)则是其逆过程。
当我们对某个字段使用 transient
关键字修饰时,该字段将不会参与序列化过程。由于该字段在序列化后的字节流中并不存在,因此在反序列化时会使用其默认值(如 null
或 0
)来初始化。
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
}
在上面的类中,我们将 description
和 copies
字段标记为 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
字段被正确保存和恢复,而 description
和 copies
字段则被设置为其默认值(分别为 null
和 0
),而非原始值。
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