一个类通过实现 java.io.Serializable 接口来启用序列化。未实现此接口的类,其任何状态都不会被序列化或反序列化。一个可序列化类的所有子类型本身也是可序列化的。该序列化接口没有方法或字段,仅用于标识可序列化的语义。为了允许非序列化类的子类型能够被序列化,子类型可以承担保存和恢复其超类型的公共( public )、受保护( protected )以及(如果可访问)包( package )字段状态的责任。只有当该子类型所扩展的类拥有一个可访问的无参数构造函数来初始化类的状态时,子类型才可以承担此责任。如果不是这种情况,那么声明一个类为 Serializable 就是错误的。这个错误将在运行时被检测到。在反序列化过程中,非序列化类的字段将使用该类的公共或受保护的无参数构造函数进行初始化。这个无参数构造函数必须对正在序列化的子类是可访问的。可序列化子类的字段将从流中恢复。在遍历对象图时,可能会遇到不支持 Serializable 接口的对象。在这种情况下,将抛出不可序列化异常( NotSerializableException ),并会标识出该非序列化对象的类。在序列化和反序列化过程中需要特殊处理的类,必须实现具有如下确切签名的特殊方法:
private void writeObject(java.io.ObjectOutputStream out) throws IOException
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;
private void readObjectNoData() throws ObjectStreamException;writeObject 方法负责写入其特定类的对象状态,以便对应的 readObject 方法可以恢复它。可以通过调用 out.defaultWriteObject 来调用保存对象字段的默认机制。该方法不需要关心属于其超类或子类的状态。状态的保存是通过使用 writeObject 方法将各个字段写入 ObjectOutputStream ,或者使用 DataOutput 接口所支持的基本数据类型的方法来完成的。readObject 方法负责从流中读取并恢复类的字段。它可以调用 in.defaultReadObject 来调用恢复对象的非静态( non-static )和非瞬态( non-transient )字段的默认机制。defaultReadObject 方法使用流中的信息,将流中保存的对象的字段分配给当前对象中对应名称的字段。这处理了类在演化过程中添加了新字段的情况。该方法不需要关心属于其超类或子类的状态。readObjectNoData 方法负责在序列化流没有将给定类列为被反序列化对象的超类时,为其特定类初始化对象的状态。在以下情况可能发生:接收方使用的反序列化实例的类版本与发送方不同,并且接收方的版本扩展了发送方版本所未扩展的类;或者序列化流遭到了篡改。因此,即使面对“恶意”或不完整的源流,readObjectNoData 对于正确初始化反序列化对象也很有用。需要在将对象写入流时指定替代对象的可序列化类,应该使用以下确切签名实现这个特殊方法:
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
如果此方法存在,并且可以从正在序列化的对象所属类中定义的方法访问到它,那么序列 化过程就会调用这个 writeReplace 方法。因此,该方法可以具有私有( private )、受保护( protected )或包私有( package-private )的访问权限。子类对此方法的访问遵循Java的可访问性规则。需要在其实例从流中读取时指定替换对象的类,应该使用以下确切签名实现这个特殊方法:
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
这个 readResolve 方法遵循与 writeReplace 相同的调用规则和可访问性规则。序列化运行时会为每个可序列化类关联一个版本号,称为 serialVersionUID,在反序列化时用于验证序列化对象的发送方和接收方是否为该对象加载了与序列化兼容的类。如果接收方为对象加载的类的 serialVersionUID 与对应发送方类的 serialVersionUID 不同,那么反序列化将会导致 InvalidClassException 。一个可序列化类可以通过声明一个名为 "serialVersionUID" 的字段来显式声明自己的 serialVersionUID,该字段必须是静态( static )、最终( final )且为 long 类型:
ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;如果一个可序列化类没有显式声明 serialVersionUID,那么序列化运行时将根据该类的各个方面计算一个默认的 serialVersionUID 值,如Java™对象序列化规范中所述。但是,强烈建议所有可序列化类都显式声明 serialVersionUID 值,因为默认的 serialVersionUID 计算对可能因编译器实现而异的类细节高度敏感,从而可能在反序列化时导致意外的 InvalidClassException 。因此,为了保证在不同的 Java 编译器实现中获得一致的 serialVersionUID 值,可序列化类必须声明显式的 serialVersionUID 值。同样强烈建议尽可能在显式 serialVersionUID 声明中使用 private 修饰符,因为此类声明仅适用于直接声明它的类—— serialVersionUID 字段作为继承成员是没有用处的。数组类不能声明显式的 serialVersionUID ,因此它们总是具有默认的计算值,但数组类免除了匹配 serialVersionUID 值的要求。另请参阅:ObjectOutputStream, ObjectInputStream, ObjectOutput, ObjectInput, Externalizable
public interface Serializable {}