自定义Java的序列化与反序列化
阅读原文时间:2021年04月20日阅读:1

首先必须实现java.io.Serializable,即使子类实现了此接口,父类仍需实现此接口,除非子类对父类状态进行了自定义的序列化处理。

[b]1. 实现接口java.io.Externalizable[/b]

void [b]writeExternal[/b](ObjectOutput out) throws IOException;
void [b]readExternal[/b](ObjectInput in) throws IOException, ClassNotFoundException;

注意实现此接口,对象必须有public声明的空构造器。

[b]2. 实现方法writeObject和readObject[/b]

private void writeObject(ObjectOutputStream os) throws IOException
private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException

它们有个好处是可以调用以下接口先进行默认的Java序列化,再进行自定义的序列化。
[b]ObjectOutputStream[/b].defaultWriteObject()和[b]ObjectInputStream[/b].defaultReadObject()

实现这些方法接口,对象可不需要空构造器,且可以是private的。

注意如果实现了接口java.io.Externalizable,这两个方法且父类的这两方法都不会被调用。否则父类的这两方法先被调用,然后才是子类。

JDK源码参考: java.util.ArrayList

[b]3. 实现方法writeReplace和readResolve[/b]

private Object writeReplace()
private Object readResolve()

通过它们可以返回不同的类型。
对于writeReplace,仍然需要对新类型进行序列化。
对于readResolve,其调用在readExternal或readObject之后。

JDK源码参考: java.util.EnumSet

[b]4. 默认序列化自定义包括关键字transient和静态字段名serialPersistentFields[/b]

transient 用于指定哪个字段不被默认序列化,如public transient int a;
serialPersistentFields 用于指定哪些字段需要被默认序列化,如

private static final ObjectStreamField[] serialPersistentFields =  {    new ObjectStreamField("name", String.class),     new ObjectStreamField("a", Integer.TYPE) };

如果同时定义了serialPersistentFields与transient,transient会被忽略。

[b]5. serialVersionUID[/b]
序列化的时候会读写serialVersionUID并做出校验,没做如下定义的话会根据类签名信息进行生成,包括类名、非私有构造器、非私有类方法、非私有静态类属性,但不包括继承层次,也就是继承层次发生变化也不会报类错误,但是父类的状态信息将得不到反序列化。
private static final long serialVersionUID = 2184568476863030694L;

[b]6. 相关异常[/b]
java.io.NotSerializableException
java.lang.ClassNotFoundException
java.io.InvalidClassException (如serialVersionUID校验失败,原生字段类型不一致等)
java.lang.ClassCastException (如自定义属性类型或自身类型不一致)
java.io.StreamCorruptedException
java.io.EOFException (自定义序列化时试图读取更多的消息)