1.3 序列化版本号

从零开始读 Java 源码 第一章 接口篇 Serializable(3)_反序列化

序列化操作运行时组织序列化类是通过版本号来进行的,叫做 serialVersionUID, 用来进行反序列化校验发送者和接收者序列化对象有加载类,兼容关于相应的对象。如果接收者加载了一个类的对象和请求放的 serialVersionUID 不一致,那么反序列化过程就会产生一个 InvalidClassException 异常。一个序列化对象可以声明它自己的全局变量命名为 serialVersionUID 并且这个对象它必须是使用 static final long 修饰的。

任意访问修饰符 static final long serialVersionUID = 42L;

如果一个序列化类没有明确的声明一个 serialVersionUID, 然后序列化操作运行时会计算一个默认的 serialVersionUID 值给这个类基于这个类各种不同的方面, 想 Java™ 对象序列化指定的描述那样。但是,强烈推荐所有的序列化类明确声明一个 seralVersionUID 值, 虽然这个默认的 serialVersionUID 能根据类详细信息十分高效的生成 serialVersionUID 但是可能对于编译实现方面耦合度很高,反序列化过程可能造成未预计的 InvalidClassException。因此,为了保证无论使用何种的java编译器下,都有一个准确的 seralVersionUID 的值,实现序列化接口的类必须明确的给出 serialVersionUID 的值。 如果在继承关系中这个 serialVersionUID 没什么用,强烈建议在条件允许的情况下明确声明 serailVersionUID 使用 private 修饰符,这样就可以在继承的时候,这样计算值不会相互干扰,每个类都有对应的计算值。特殊情况是数组,如果使用 serailVersionUID 来匹配数组的话,会浪费空间。

整理一下内容:

  1. 如果不希望在反序列化过程中出现 InvalidClassException 异常,那就要声明一个唯一ID:

private  static final long serialVersionUID = 42L;

这样的序列号;

  1. 虽然可以自动生成,但是自动生成的序列号跨平台的效果不好,不同系统间不兼容;
  2. 虽然能用非private修饰符进行修饰,但是可能会造成父子类的干扰,别用。

1.3.1 序列化反序列化不同编码测试

这一段其实是说明了序列化操作 serialVersionUID 的用途,就是用于校验接收端和发送端的对象,进行反序列化的识别号。那就试下序列化后,把这个值改了再反序列化是什么后果。

序列化:

从零开始读 Java 源码 第一章 接口篇 Serializable(3)_开发语言_02

从零开始读 Java 源码 第一章 接口篇 Serializable(3)_反序列化_03

反序列化:

从零开始读 Java 源码 第一章 接口篇 Serializable(3)_序列化_04

从零开始读 Java 源码 第一章 接口篇 Serializable(3)_java_05

从零开始读 Java 源码 第一章 接口篇 Serializable(3)_java_06

果然报错,还有版本提示

1.3.2 不写编码 serialVersionUID 测试

从零开始读 Java 源码 第一章 接口篇 Serializable(3)_反序列化_07

从零开始读 Java 源码 第一章 接口篇 Serializable(3)_序列化_08

然后在B里面加个属性temp和它的getter与setter

从零开始读 Java 源码 第一章 接口篇 Serializable(3)_后端_09

从零开始读 Java 源码 第一章 接口篇 Serializable(3)_后端_10

从零开始读 Java 源码 第一章 接口篇 Serializable(3)_反序列化_11

然后就发现发生了这个错误。

同样的方式,加了序列号是可以解析的。