问题的历史:
Java从一开始就支持通过Serializable接口对对象进行序列化的机制。有时需要保存对象的状态,但并非所有字段都应该被序列化——例如,它们可能涉及安全性或动态计算。因此,设计了transient修饰符。
问题:
如果完全序列化对象,而不考虑各个字段的特殊性,可能会导致泄露私有或不适合序列化的数据。此外,序列化和反序列化大型或临时字段(例如,线程、与数据库的连接)可能会导致错误或性能下降。
解决方案:
对不应在对象保存时被序列化的字段使用transient修饰符。这些字段在序列化机制中会被忽略,而在反序列化时将获得默认值(例如,对于引用类型为null,对于数字为0)。
代码示例:
import java.io.*; class User implements Serializable { private String username; private transient String password; // 不会被序列化! public User(String username, String password) { this.username = username; this.password = password; } }
关键特点:
静态字段可以是transient吗?
回答:可以将字段声明为static transient,但这没有意义:静态字段本身不会被序列化,因为它们属于类而不是对象。
可以对transient字段进行完全控制序列化吗?
回答:是的,通过实现private void writeObject(ObjectOutputStream out)和private void readObject(ObjectInputStream in)方法,可以在需要时手动序列化transient字段。
代码示例:
private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); // out.writeObject(password); // 如果需要明确序列化transient字段 }
反序列化后final transient字段会怎样?
回答:final transient字段同样会获得默认值(通常为零或null),但之后无法更改,除非使用特殊的技巧,这通常会导致错误。
序列化了一个具有transient字段的DatabaseConnection对象。反序列化后尝试调用这个连接的方法——结果得到NullPointerException。
优点:
缺点:
序列化了一个带有transient密码的User对象,反序列化时在readObject中询问用户密码或恢复连接。
优点:
缺点: