反序列化如何处理普通类型和枚举类型?
反序列化如何处理枚举类型?
提问:这段代码输出什么?
import java.io.*;
enum Status {
ACTIVE, INACTIVE, SUSPENDED
}
// 使用枚举的类
class EnumUser implements Serializable {
private Status status;
private String name;
public EnumUser(Status status, String name) {
this.status = status;
this.name = name;
}
// 测试代码
public static void main(String[] args) throws Exception {
// 序列化
EnumUser user = new EnumUser(Status.ACTIVE, "name");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(user);
// 反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
EnumUser restoredUser = (EnumUser) ois.readObject();
// 验证是否是同一个枚举实例
System.out.println(user.status == restoredUser.status); // 输出 ①
System.out.println(user.name == restoredUser.name); // 输出 ②
}
}
枚举类型的反序列化
枚举类型在反序列化过程中确实有特殊处理机制,这与它们的final性质和单例特性有关。以下是Java如何处理枚举的反序列化:
-
特殊序列化格式:枚举实例序列化时,只保存枚举类名和枚举常量名,而不是完整的对象状态。
-
特殊的反序列化逻辑:反序列化枚举时,Java不会创建新的实例,而是查找已存在的枚举常量。具体来说:
ObjectInputStream的readEnum方法负责处理枚举反序列化- 它使用
Enum.valueOf()方法查找与序列化数据匹配的已定义枚举常量 - 返回这个已存在的枚举实例,而不是创建新对象
-
保证单例性:这种机制确保了反序列化不会破坏枚举的单例特性,即使多次反序列化同一个枚举值,得到的都是同一个Java实例。
-
安全性:这也防止了伪造枚举实例的可能性。如果序列化数据包含一个不存在的枚举常量名,反序列化会抛出
IllegalArgumentException。
这种特殊处理确保了枚举在整个系统中的一致性和类型安全性,即使经过序列化和反序列化操作。这也是为什么枚举常常被推荐用于实现单例模式的序列化安全版本。
String 类型的反序列化
-
不使用字符串常量池机制:虽然 Java 有字符串常量池(String pool)来实现相同字面量字符串的复用,但在反序列化时,是先创建一个新的 String 对象,而不是从常量池获取。
-
重新构造对象:反序列化会创建全新的 String 对象实例,而不是引用到已有的实例。
-
没有特殊处理:与枚举不同,String 没有像枚举那样的特殊反序列化处理逻辑。
总结
① 是 true,② 是 false。
这个例子的输出结果表明,虽然反序列化后的字符串内容相同,但它是一个全新的对象(== 返回 false)。
而与此相反,枚举类型在反序列化时通过 Enum.valueOf() 方法获取已有的实例,确保了引用相等性,即使是反序列化后也会指向同一个内存地址。
这是 Java 序列化系统设计上的差异,反映了枚举作为单例的特性和 String 作为普通对象的不同处理方式。

浙公网安备 33010602011771号