JAVA序列号的serialVersionUID

serialVersionUID 的规范

Serializable 和 Externalizable

Java类经过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将没法进行序列化或反序列化。可序列化类的全部子类型自己都是可序列化的。java

若是读者看过Serializable的源码,就会发现,他只是一个空的接口,里面什么东西都没有。Serializable接口没有方法或字段,仅用于标识可序列化的语义。可是,若是一个类没有实现这个接口,想要被序列化的话,就会抛出java.io.NotSerializableException异常。eclipse

序列号在写成二进制流的时候,会调用以下的方法:code

Externalizable继承自Serializable,该接口中定义了两个抽象方法:writeExternal()与readExternal()。对象

当使用Externalizable接口来进行序列化与反序列化的时候须要开发人员重写writeExternal()与readExternal()方法。不然全部变量的值都会变成默认值。blog

transient 不须要被序列化

transient 关键字的做用是控制变量的序列化,在变量声明前加上该关键字,能够阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。继承

什么是 serialVersionUID

序列化是将对象的状态信息转换为可存储或传输的形式的过程。咱们都知道,Java对象是保存在JVM的堆内存中的,也就是说,若是JVM堆不存在了,那么对象也就跟着消失了。接口

而序列化提供了一种方案,可让你在即便JVM停机的状况下也能把对象保存下来的方案。就像咱们平时用的U盘同样。把Java对象序列化成可存储或传输的形式(如二进制流),好比保存在文件中。这样,当再次须要这个对象的时候,从文件中读取出二进制流,再从二进制流中反序列化出对象。ip

虚拟机是否容许反序列化,不只取决于类路径和功能代码是否一致,一个很是重要的一点是两个类的序列化 ID 是否一致,这个所谓的序列化ID,就是咱们在代码中定义的serialVersionUID。内存

这是由于,在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,若是相同就认为是一致的,能够进行反序列化,不然就会出现序列化版本不一致的异常,便是InvalidCastException。开发

为何必须设置默认的 serialVersionUID 值

若是咱们没有在类中明确的定义一个serialVersionUID的话,看看会发生什么。

尝试修改上面的demo代码,先使用如下类定义一个对象,该类中不定义serialVersionUID,将其写入文件。

而后咱们修改User1类,向其中增长一个属性。在尝试将其从文件中读取出来,并进行反序列化。

执行结果:

java.io.InvalidClassException: com.hollis.User1; local class incompatible: stream classdesc serialVersionUID = -2986778152837257883, local class serialVersionUID = 7961728318907695402

一样,抛出了InvalidClassException,而且指出两个serialVersionUID不一样,分别是-2986778152837257883和7961728318907695402。

从这里能够看出,系统本身添加了一个serialVersionUID。

因此,一旦类实现了Serializable,就建议明确的定义一个serialVersionUID。否则在修改类的时候,就会发生异常。

serialVersionUID有两种显示的生成方式:

一种是默认的1L,好比:

private static final long serialVersionUID = 1L;

另一种是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,好比:

private static final long serialVersionUID = xxxxL;

后面这种方式,能够借助IDE生成,后面会介绍。

背后原理

为了简化代码量,反序列化的调用链以下:

在initNonProxy中 ,关键代码以下:

在反序列化过程当中,对serialVersionUID作了比较,若是发现不相等,则直接抛出异常。

深刻看一下getSerialVersionUID方法:

在没有定义serialVersionUID的时候,会调用computeDefaultSUID 方法,生成一个默认的serialVersionUID。

这也就找到了以上两个问题的根源,实际上是代码中作了严格的校验,而且在未定义的时候自动生成了一个serialVersionUID。

IDEA提示

为了确保咱们不会忘记定义serialVersionUID,能够调节一下Intellij IDEA的配置,在实现Serializable接口后,若是没定义serialVersionUID的话,IDEA(eclipse同样)会进行提示:

而且能够一键生成一个:

固然,这个配置并非默认生效的,须要手动到IDEA中设置一下:

在图中标号3的地方(Serializable class without serialVersionUID的配置),打上勾,保存便可。

相关文章
相关标签/搜索