序列化

Wu Jun 2020-01-03 19:43:49
Categories: > > Tags:

Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,写出到输出流中,并在之后将其读回。整个过程都是 Java 虚拟机独立的。

1 保存和加载序列化对象

一个类的对象要想序列化成功,必须满足两个条件:

只有需要写出对象时才使用 witeObject/readObject,对于基本类型值,使用诸如 writeInt/readInt 或 writeDouble/readDouble (对象流类都实现了 DataInput/DataOutput 接口)

1.1 序列化对象

ObjectOutputStream 类 writeObject(Object x) 方法:序列化一个对象,并将它发送到输出流。

ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("employee.txt"));
Manager boss = new Manager("Carl Cracker",8000,1987,12,25); 
out.writeObject(harry); 

1.2 反序列化对象

ObjectInputStream 类 readObject() 方法:从流中取出下一个对象,并将对象反序列化。

ObjectInputStream in = new ObjectInputStream(new FileInputStream("employee.txt"));
Employee e1 = (Employee) in.readObject();

2 对象序列化的文件格式

对象序列化是以特殊的文件格式存储对象数据的

示例:

3 修改默认的序列化机制

3.1 避免序列化

不可序列化的类和不需要序列化的域,需要标记成是 transient 的。瞬时的域在序列化时会被跳过。

3.2 手动序列化

可序列化的类可以定义具有下列签名的方法,之后数据域就再不会被自动序列化,取而代之的是调用这些方法。

private void readObject(ObjectInputStream in) throws IOException,ClassNoutFoundException;
private void writeObject(ObjectOutputStream out) throws IOException;

3.3 扩展序列化

除了让序列化机制来保存和恢复对象数据,类还可以定义它自己的机制。为了做到这一点,这个类必须实现 Externalizable 接口,这需要定义两个方法:

public void readExternal(ObjectInputStream in) throws IOException ,ClassNotFoundException;
Public void writeExternal(ObjectOutputStream out) throws IOException;

4 序列化单例和类型安全的枚举

单例的类可以用==操作符来测试对象的等同性。但及时构造器私有,序列化机制也可以创建新的对象!这时的新对象通过==就不相等了。

为了解决这个问题,需要定义另外一种称为 readResolve 的特殊序列化方法。在这个方法中检查 value 并返回合适的单例对象。

5 版本管理

一个类的所有较新的版本都必须把 serialVersionUID 常量定义为与最初版本的指纹相同,这样序列化系统就可以读入这个类的对象的不同版本。

如果这个类只有方法产生了变化,那么在读入新对象数据时是不会有任何问题的。但是,如果数据域产生了变化,那么就可能会有问题。

对象输入流会将这个类当前版本的数据域与被序列化的版本中的数据域进行比较(只考虑非瞬时和非静态的数据域)。

6 为克隆使用序列化

将对象序列化到输出流中,然后将其读回。这样产生的新对象是对现有对象的一个深拷贝。在此过程中,我们不必将对象写出到文件中,因为可以用 ByteArrayOutputStream 将数据保存到字节数组中。

要想得到 clone 方法,只需扩展 SerialCloneable 类。

我们应该当心这个方法,通常会比显式地构建新对象并复制或克隆数据域的克隆方法慢得多。