15.8.6 另一种自定义序列化机制

15.8.6 另一种自定义序列化机制

Java还提供了另一种序列化机制,这种序列化方式完全由程序员决定存储和恢复对象数据。要实现该目标,Java类必须实现Externalizable接口。

Externalizable接口

Externalizable接口里定义了如下两个方法。

方法 描述
void writeExternal(ObjectOutput out) 需要序列化的类实现这个writeExternal方法来保存对象的状态。
该方法
  • 调用DataOutput的方法来保存基本类型的实例变量值,
  • 调用ObjectOutputwriteObject()方法来保存引用类型的实例变量值。
void readExternal(ObjectInput in) 需要序列化的类实现这个readExternal()方法来实现反序列化。
该方法
  • 调用DataInput的方法来恢复基本类型的实例变量值,
  • 调用ObjectInputreadObject()方法来恢复引用类型的实例变量值。
  • DataInputObjectInput的父接口
  • DataOutputObjectOutput的父接口

程序 实现Externalizable接口自定义序列化

实际上,采用实现Externalizable接口方式的序列化与前面介绍的自定义序列化非常相似,只是**Externalizable接口强制自定义序列化**。
下面的Person类实现了Externalizable接口,并且实现了该接口里提供的两个方法,用以实现自定义序列化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.io.*;
public class Person implements java.io.Externalizable {
private String name;
private int age;
// 注意必须提供无参数的构造器,否则反序列化时会失败。
public Person() {
}
public Person(String name, int age) {
System.out.println("有参数的构造器");
this.name = name;
this.age = age;
}
// 此处省略getter和setter方法,请自己补上

public void writeExternal(java.io.ObjectOutput out)
throws IOException {
// 将name实例变量的值反转后写入二进制流
out.writeObject(new StringBuffer(name).reverse());
out.writeInt(age);
}
public void readExternal(java.io.ObjectInput in)
throws IOException, ClassNotFoundException {
// 将读取的字符串反转后赋给name实例变量
this.name = ((StringBuffer) in.readObject()).reverse().toString();
this.age = in.readInt();
}
}

上面程序中的Person类实现了java.io.Externalizable接口,该Person类还实现了readExternalwriteExternal两个方法,这两个方法除方法签名和readObjectwriteObject两个方法的方法签名不同之外,其方法体完全一样。

序列化和反序列化方法相同

如果程序需要

  • 序列化实现Externalizable接口的对象,一样调用ObjectOutputStreamwriteObject()方法输出该对象即可;
  • 反序列化实现Externalizable接口的对象,则调用ObjectInputStreamreadObject()方法

必须提供 public的 无参数构造器

需要指出的是,当使用Externalizable机制反序列化对象时,程序会先使用public的无参数构造器创建实例,然后才执行readExternal()方法进行反序列化,因此实现Externalizable的序列化类必须提供public的无参数构造器

表15.2两种序列化机制的对比

实现Serializable接口 实现Externalizable接口
系统自动存储必要信息 程序员决定存储哪些信息
Java内建支持,易于实现,只需实现该接口即可,无须任何代码支持 仅仅提供两个空方法,实现该接口必须实现g该接口的两个空方法
性能略差 性能略好

虽然实现Externalizable接口能带来一定的性能提升,但由于实现Externalizable接口导致了编程复杂度的增加,所以大部分时候都是采用实现Serializable接口方式来实现序列化。

对象序列化需要注意的地方

关于对象序列化,还有如下几点需要注意。

  1. 对象的类名实例变量(包括基本类型、数组、对其他对象的引用)都会被序列化;
    1. 方法、类变量(即static修饰的成员变量)、transient实例变量(也被称为瞬态实例变量)都不会被序列化
  2. 实现Serializable接口的类如果需要让某个实例变量不被序列化,则可在该实例变量前加transient修饰符,而不是加static关键字。虽然static关键字也可达到这个效果,但static关键字不能这样用。
  3. 保证序列化对象的实例变量类型也是可序列化的,否则需要使用transient关键字来修饰该实例变量,要不然,该类是不可序列化的。
  4. 反序列化对象时必须有序列化对象的class文件。
  5. 当通过文件、网络来读取序列化后的对象时,必须按实际写入的顺序读取。