详解Java序列化和反序列化中的writeExternal方法和readExternal方法
保留所有版权,请引用而不是转载本文(原文地址 https://yeecode.top/blog/64/ )。
要表明一个类的对象是可序列化的则必须要继承Serializable接口或者Externalizable接口,而且Externalizable接口是Serializable接口的子接口。这些内容我们在前面的文章中均已经介绍过,还给出了继承Serializable接口进行序列化和反序列化的示例。
继承Serializable接口实现序列化和反序列化是非常简单的,目标类除了继承Serializable接口外不需要任何其他的操作,整个序列化和反序列化的过程由Java内部的机制完成。而继承Externalizable接口实现序列化和反序列化则支持自定义序列化和反序列化的方法。Externalizable接口包含两个抽象方法:
void writeExternal(ObjectOutput out)
: 该方法在目标对象序列化时调用。方法中可以调用DataOutput(入参ObjectOutput的父类)的方法来保存其基本值,或调用ObjectOutput的writeObject方法来保存对象、字符串和数组。void readExternal(ObjectInput in)
:该方法在目标对象反序列化时调用。方法中调用DataInput(入参ObjectInput的父类)的方法来恢复其基础类型,或调用readObject来恢复对象、字符串和数组。需要注意的是readExternal方法读取数据时,必须与writeExternal方法写入数据时的顺序和类型一致。
我们通过示例来说明writeExternal方法和readExternal方法的作用。在下面代码中,我们设置了UserModel02类的writeExternal方法和readExternal方法。
public class UserModel02 implements Externalizable {
private static final long serialVerisionUID = 1L;
private Integer id;
private String name;
private String description;
// 省略属性的get、set方法
@Override
public void writeExternal(ObjectOutput out) throws IOException {
System.out.println("writeExternal doing ...");
out.write(id); // DataOutput中的方法
out.writeObject(name + "(from writeExternal)");
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
System.out.println("readExternal doing ...");
id = in.read();
name = (String) in.readObject();
System.out.println("name in file is:" + name);
name = name + "(from readExternal)";
}
}
然后我们对上述类的实例进行序列化和反序列化,过程如下所示。
private static void demo02() throws Exception {
System.out.println("run demo02:");
UserModel02 userModel02 = new UserModel02();
userModel02.setId(1);
userModel02.setName("易哥");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("m2.tempdata"));
oos.writeObject(userModel02);
oos.flush();
oos.close();
System.out.println("↑write;↓read");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("m2.tempdata"));
UserModel02 newUser = (UserModel02) ois.readObject();
System.out.println("newUser:" + newUser.getId() + "-" + newUser.getName());
System.out.println();
}
最终的到了下图所示的运行结果。
可见,对于实现了Externalizable接口的类,会在对象的序列化时调用writeExternal方法,而在对象的反序列化时调用readExternal方法。
因为我们可以通过自定义writeExternal方法和readExternal方法的具体实现,来控制对象的序列化和反序列化行为。这也使得继承Externalizable接口实现序列化和反序列化更为自由和强大。
以上内容均参考《通用源码阅读指导书——MyBatis源码详解》一书。
这是一本以MyBatis的源码为实例讲述源码阅读方法的书籍,并且附带有示例项目源码,MyBatis的全中文注解。书籍还总结了大量的编程知识和架构经验,对提升编程和架构能力十分有用。
书中的“22.3.3 懒加载功能对序列化和反序列化的支持”章节介绍了懒加载中的序列化和反序列化操作,大家可以前往阅读。
在接下来的文章中,我们会介绍序列化和反序列化中的writeReplace方法和readResolve方法,还会进一步介绍序列化和反序列化过程中各个方法的执行顺序。
可以访问个人知乎阅读更多文章:易哥(https://www.zhihu.com/people/yeecode),欢迎关注。