一般而言,定义实体类时会实现 Serializable
接口,同时还要显示指定serialVersionUID
的值。
为什么要有这些操作呢?这里就试图简单理解。
1. 序列化与反序列化
序列化:把对象转换为字节序列的过程称为对象的序列化。
反序列化:把字节序列恢复为对象的过程称为对象的反序列化。
从概念上看,序列化操作只是针对于对象层面的,包含对象的成员变量和成员方法。那么被static修饰的成员变量呢?结论是被static修饰的属性不会被序列化。
但这并不意味着static修饰的变量会缺失。这个变量不管被什么修饰词修饰,始终还算对象里边的成员变量。只是可能存在变量值不一致的情况,因为无法序列化代表对象的该变量的值无法硬性写入字节序列(有点饶,描述serialVersionUID进一步阐述)
2. Serializable接口
一个对象序列化的接口,一个类只有实现了Serializable接口,它的对象才能被序列化。
日常接触的String类,其底层也实现了Serializable接口,所以JSON字符串才能在前后端自由传输。
但观察Serializable接口就会发现,接口内部啥也没有,就是说,它是一个空接口。其实,可以将它理解为一个标识接口。实现这个接口,就是给JVM看的,JVM会帮助当前类实现序列化(或反序列化)的具体操作。
Serializable接口就是Java提供用来进行高效率的异地共享实例对象的机制,实现这个接口即可。
3. serialversionUID变量
通常的写法是:
private static final long serialVersionUID = 12356L; |
显示声明这个变量并赋予字面量的作用是啥?保证类的一致性。如果类发生变化,依然能够对旧类的对象(已经序列化)执行反序列化操作。
很多时候我们未显示声明该变量,程序照常运行。实际上,是JVM在序列化时根据属性自动生成一个serialVersionUID,然后与属性一起序列化。在反序列化时,JVM会再根据属性自动生成一个新版serialVersionUID,然后将新serialVersionUID与旧serialVersionUID比较,如果一致,则反序列化成功,否则报错。
类发生变化(旧类与新类),JVM自动生成的serialVersionUID值就不一致,所以就可能出现问题。
4. serialversionUID变量也被static修饰
前面提到,被static修饰的属性不会被序列化,但serialversionUID变量也被static修饰确可以被序列化,这不是打脸吗?
答案是,你用static修饰的那个变量并没有被序列化,只不过是工具变量拿来传值的。JVM 在序列化对象时会自动生成一个 serialVersionUID, 然后将我们显示指定的 serialVersionUID 属性值赋给自动生成的 serialVersionUID。
至于其他的static修饰的属性,这些属性是跟着类走的。一种合理的解释是,序列化是针对对象而言的, 而 static 属性优先于对象存在, 随着类的加载而加载, 所以不会被序列化。
5. 什么时候需要序列化和反序列化
答案是:对内存中的对象进行数据(指对象的状态信息)传输(通信)与数据存储(持久化)。很多时候我们可能并不是将整个对象存储或者传输,而是其中的字段信息,比如说将数据保存在数据库。这个过程每个字段的变量类型都应该是可序列化的,也即实现了Serializable接口。
我们经常这么用,但不一定想到原来它们都默默实现了Serializable接口。