Java基础之IO篇——Serializable
这篇博客是为了记录刚开始写java时的疑惑:为什么数据库表对应的java实体类都implements Serializable 接口?且声明了一个serialVersionUID = -113873284728278L?
要知道为什么实体类要去实现这个接口,首先要知道这个接口是干嘛的,有什么作用。Serializable是java.io包下的接口,查看java官方类库文档对Serializable接口的说明是这样的:
Serializability of a class is enabled by the class implementing the java.io.Serializable interface. Classes that do not implement this interface will not have any of their state serialized or deserialized. All subtypes of a serializable class are themselves serializable. The serialization interface has no methods or fields and serves only to identify the semantics of being serializable.
大意就是实现了Serializable接口的类及其子类的对象可以被序列化和反序列化,Serializable接口没有方法和字段,只用于标识可序列化语义。
那么什么是序列化和反序列化?
序列化:将对象转化为字节序列的过程。
反序列化:将字节序列转化为对象的过程。
将对象转化为字节序列主要是用于对象在内存和物理硬盘(磁盘空间)之间或者网络上的传输。例如,一个类被10万用户并发访问,每一个访问都会创建一个该类的对象,如果该类的生命周期较长,短时间内不会被GC销毁,那么内存中该对象会随着用户访问逐渐累积,如果累积到内存不足,那么就需要将对象存入磁盘空间(通常以文件的形式),在需要使用时再从磁盘中读取到内存中使用。典型的列子就是web 服务器中的Session对象。那么,另一种场景就是数据库表的增删改查了,前端用户新提交一个表单,比如网站用户注册,那么应用需要将用户注册的信息(用户名、昵称、密码等)永久保存起来,此时就需要将内存中该用户信息的对象永久保存起来以便用户后续再登录应用时使用(通常持久化到数据库),所以开篇第一个问题——为什么数据库表的实体类都实现了Serializable接口,就得到答案了。
需要注意的是:
java官方类库文档中还有说明,如果一个非可序列化类的子类需要被声明为可序列化(implements Serializable)的,那么其父类必须有可被访问的无参构造函数,否则运行时会报错。
既然知道了为什么要声明一个实体类为Serializable,那么第二个问题就是要明确声明一个serialVersionUID的作用是什么?
关于serialVersionUID的作用,官方文档也有说明:
序列化过程中会将可序列化类与一个serialVersionUID相关联,在反序列化时用来校验序列化对象的发送方和接收方是否已经加载了与该对象兼容的类。如果接收方为对象加载了一个与发送方类具有不同serialVersionUID的类那么反序列化将会导致InvalidClassException。也就是说这个serialVersionUID就是用来标识类的兼容性的。
如果可序列化类未显式声明 serialVersionUID,则序列化运行时将根据该类的各个方面计算该类的默认 serialVersionUID 值,这种情况下只有同一次编译的类才会生成同样的serialVersionUID。因为默认的 serialVersionUID 计算对可能因编译器实现而异的类详细信息高度敏感,因此可能会在反序列化期间导致意外的 InvalidClassException,因此可序列化的类最好显示地声明一个自己的serialVersionUID,且必须声明为 static final long的,最好也是private 的,serialVersionUID 字段作为继承成员没有用。
private static final long serialVersionUID = 42L;



