序列化是把对象转换为字节序列的过程;反序列化是把字节序列恢复为对象的过程。对象的序列化主要有两个目的:一是将对象转化成字节后保存在存储介质中,即为了持久化对象;二是将对象转化成字节后在网络上传输,即为了传输对象。而与之对应,将字节还原为对象的过程就是反序列化。
在 Java中,要表明一个类的对象是可序列化的,则必须继承 Serializable接口或其子接口 Externalizable接口。Externalizable接口的使用稍复杂,将在 22.1.3节中介绍,这一节先介绍 Serializable接口。
Serializable 接口的使用非常简单,只要一个类实现了该接口,便表明该类的对象是可序列化的,而不需要增加任何方法。
序列化与反序列化过程中,要面临版本问题。例如,将一个 User类的对象 user1持久化到了硬盘中,然后增删了 User类的属性,那么此时还能将持久化在硬盘中的user1对象的序列还原成一个新的 User类的对象吗?
该问题的回答需要涉及 Serializable接口的 serialVersionUID字段。serialVersionUID字段叫作序列化版本控制字段,我们经常会在实现了 Serializable接口的类中见到它。
在反序列化过程中,如果对象字节序列中的 serialVersionUID与当前类的该值不同,则反序列化失败,否则成功。
如果没有显式地为一个类定义 serialVersionUID属性,系统就会自动生成一个。自动生成的序列化版本控制字段与类的类名、类及其属性修饰符、接口及接口顺序、属性、构造函数等相关,其中任何一项的改变都会导致 serialVersionUID发生变化。
因此,对于上面所述的 user1对象的序列能否还原成一个新的 User类的对象,需要分情况进行讨论。
- 如果旧 User类和新 User类中均有 serialVersionUID字段,且其值一样,则持久化在硬盘中的 user1对象的序列可以还原成一个新的 User类的对象。还原的过程中,新User类对象中新增的属性值为 null。
- 如果旧 User 类和新 User 类中一方不含 serialVersionUID 字段,或两方都含有serialVersionUID字段但其值不同,则无法反序列化,反序列化过程中会报出序列号版本不一致异常(InvalidClassException)。
在使用时,一般都会为实现Serializable接口的类显式声明一个serialVersionUID。这样便可以:
- 在希望类的版本间实现序列化和反序列化的兼容时,保持 serialVersionUID值不变。
- 在希望类的版本间序列化和反序列化不兼容时,确保 serialVersionUID值发生变化。