薯拾

Java 序列化

2020-09-01

对象序列化保存对象的“状态”,也就是成员变量,不会关注类中的静态变量和方法。如果对象的成员是对象则递归处理。

1. 使用场景

  • 将内存中的对象状态保存到文件或数据库;
  • 在网络上用socket传输对象;
  • 通过RMI传输对象。

2. Serializable接口

  • 任何需要被序列化的对象都需要实现此接口,此接口完全为空,只用来说明此接口实现类的对象可以被序列化。
  • 如果父类实现此接口,子类自动可序列化,不用显示实现此接口;
  • 如果子类实现而父类没实现,那么父类必须有无参构造方法(如果自定义了有参构造函数,就必须写出无参构造函数;都没有时默认生成无参构造函数),此时反序列化得到的对象的父类变量可能和当前的父类变量不同。
  • StringEnum数组可序列化,此外必须实现了此接口,才可序列化。

3. 对象读写

序列化工作通过ObjectOutputStreamObjectInputStream来完成的,使用writeObjectreadObject对对象进行读写,对于基本类型,可采用readInt|weiteInt等接口读写。

4. transient关键字

修饰类的变量使得序列化过程忽略该变量(修饰static变量不报错,没意义),此关键字标识的引用会以null返回,基本类型以对应的默认值返回。

5. serialVersionUID

Java序列化机制通过serialVersionUID判断类的版本一致性,同样的类如果serialVersionUID不同不能互相反序列化。InvalidClassException

6. 存储规则

  • 同一个对象多次存入一个文件,只是多存储一份引用;
  • 修改后的对象存入同一个文件,判断文件中已有该对象,后面只存储引用,依次读出的对象都是第一次的对象,没有修改的值。

writeObjectreadObject

1
2
3
4
5
6
7
8
private void writeObject(ObjectOutputStream out){
out.defaultWriteObject();
out.writeInt(area);
}
private void readObject(ObjectInputStream in){
in.defaultReadObject();
area = in.readInt();
}
  • 序列化类定义这两个方法必须是private(使用反射调用的)。
  • writeInt是将area单独保存到文件,而非保存成对象的属性,readInt是单独读取并设置成属性。
  • 序列化对象时,如果对象有writeObject方法,则调用,否则执行默认序列化规则;反序列化同理。

7. Externalizable接口

Externalizable继承于Serializable,当使用该接口时,序列化的细节需要由程序员去完成。

  • 实现writeExternalreadExternal方法
  • 序列化类必须有public无参构造函数(readExternal先调用无参构造函数创建对象,再自行赋值)
  • 生成文件存储格式和Serializable不太相同

8. readResolve()

用于解决反序列化后违背单例的问题。

无论是实现Serializable接口,或是Externalizable接口,当从I/O流中读取对象时,readResolve()方法都会被调用到。实际上就是用readResolve()中返回的对象直接替换在反序列化过程中创建的对象。

测试发现,使用``Externalizable时,此方法readExternal之后调用;使用Serializable时,也是在readObject`之后调用。

9. 缺点

  • 无法跨语言
  • 序列化后的码流较大
  • 序列化性能较低
Tags: java
使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

扫描二维码,分享此文章