三分钟了解标记接口 Serializable

三分钟了解标记接口 Serializable

1. 接口定义

Serializable 是一个标记接口(marker interface),没有定义任何方法。它的主要作用是表明某个类的对象可以被序列化。


2. 用途

  • 序列化支持:当一个类实现了 Serializable 接口时,表示该类的实例可以通过序列化机制转换为字节流,并在需要时通过反序列化恢复为对象。
  • 子类继承性:所有实现了 Serializable 接口的类的子类也自动支持序列化。
  • 非序列化类的处理:如果一个类未实现 Serializable 接口,则其对象无法被序列化或反序列化。

3. 序列化与反序列化的基本规则

  • 序列化:将对象的状态保存到字节流中。
  • 反序列化:从字节流中恢复对象的状态。
  • 非序列化类的子类:如果一个类未实现 Serializable 接口,但其子类实现了该接口,则子类必须负责保存和恢复父类的可访问状态(如公共、受保护或包级字段)。前提是父类必须有一个无参构造函数来初始化其状态。

4. 异常处理

  • 如果在序列化过程中遇到未实现 Serializable 接口的对象,会抛出 NotSerializableException 异常。
  • 在反序列化时,非序列化类的字段将通过其无参构造函数初始化,而序列化子类的字段则从流中恢复。

5. 特殊方法

为了支持自定义的序列化和反序列化行为,类可以实现以下特殊方法:

(1) writeObject 方法
private void writeObject(java.io.ObjectOutputStream out) throws IOException;
  • 用于在序列化过程中保存对象的状态。
  • 可以调用 out.defaultWriteObject() 来使用默认机制保存对象的非静态和非瞬态字段。
(2) readObject 方法
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;
  • 用于在反序列化过程中恢复对象的状态。
  • 可以调用 in.defaultReadObject() 来使用默认机制恢复对象的非静态和非瞬态字段。
(3) readObjectNoData 方法
private void readObjectNoData() throws ObjectStreamException;
  • 用于在反序列化过程中初始化对象的状态,即使序列化流中未列出该类作为超类。
  • 这种情况可能发生在接收方使用的类版本与发送方不同,或者序列化流被篡改时。
(4) writeReplace 方法
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
  • 用于在序列化过程中指定替代对象。
  • 例如,某些类可能希望在序列化时用另一个类的实例代替当前实例。
(5) readResolve 方法
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
  • 用于在反序列化过程中指定替代对象。
  • 例如,单例模式中的类可以通过此方法确保反序列化后返回的是唯一的实例。

6. serialVersionUID

  • 定义:每个序列化类都有一个与之关联的版本号,称为 serialVersionUID

  • 作用:用于验证序列化和反序列化过程中发送方和接收方加载的类是否兼容。

  • 声明方式

    private static final long serialVersionUID = 42L;
    
  • 推荐:强烈建议显式声明 serialVersionUID,以避免因编译器实现差异导致的 InvalidClassException 异常。

  • 默认计算:如果未显式声明 serialVersionUID,JVM 会根据类的结构计算一个默认值,但这可能导致不一致的问题。


7. 注意事项

  • 数组类:数组类无法显式声明 serialVersionUID,但它们总是使用默认计算的值,并且在反序列化时不要求匹配 serialVersionUID
  • 继承性serialVersionUID 字段不是继承的成员,因此建议将其声明为 private

8. 框架归属

Serializable 接口自 JDK 1.1 起引入,是 Java 核心语言的一部分。


总结

Serializable 接口的主要目的是作为标记接口,表明某个类支持序列化操作。虽然接口本身没有任何方法,但它定义了一套完整的规则和机制,用于控制对象的序列化和反序列化过程。开发者可以通过实现特殊方法(如 writeObjectreadObject)来自定义序列化行为,同时建议显式声明 serialVersionUID 以确保序列化的兼容性和稳定性。

posted @ 2025-04-03 15:31  皮皮是个不挑食的好孩子  阅读(47)  评论(0)    收藏  举报