在实现了Serializable接口的class中,需要声明一个long serialVersionUID,用来标明当前class的版本号,但很多人在编程时,总是不原意去声明这个serialVersionUID,又JVM自己来生成。下面来看看serialVersionUID的作用:
1、如果在序列化写 时的版本号和序列化读 时的版本号,不一致,将会有异常:java.io.InvalidClassException:local class incompatible: stream classdesc serialVersionUID = …, local class serialVersionUID = …
2、那如果在class中不声明这个属性呢?那结果可以就会变得比较诡异了:
1)、在序列化写 的时候,虚拟机A会为它计算出一个serialVersionUID,计算的方法是依据class的信息,再具体我也不清楚了。
2)、在序列化读 的时候,虚拟机B也会为class计算出一个serialVersionUID,然后做比较。
3)、那么如果两个虚拟机是不同类型的虚拟机,那么计算方法可能就不一样了,于是即使相同的class,serialVersionUID也可能会不同,不同的class的,理论上来说,也存在serialVersionUID相同的可能性,所以,serialVersionUID尽量由我们自己来指定,而不要由虚拟机来计算。
3、那如果serialVersionUID一致,而class发生了变化呢?
1)、如果虚拟机A中的AClass有一个属性,而虚拟机B中的AClass,没有这个属性,那么这个属性将被忽略,而不会有异常。
2)、如果虚拟机A中的AClass没有的属性,而在虚拟机B中多出来的属性,那么这个属性将被赋予一个缺省值,而不会有异常。
3)、如果虚拟机A中的AClass有一个属性,在虚拟机B中的AClass也有这个属性,但这个属性的类型变了,比如说int变成了long,抑或其他的变化,将会有异常:java.io.InvalidClassException:incompatible types for field …
经过序列化而产生的异常都是 java.io.InvalidClassException,不会产生java.lang.ClassCastException,两者还是有比较大的区别的,从名字上就可以看得出来。
看完你还会让JVM自己来生成serialVersionUID吗?你还加@SuppressWarnings("serial")吗?在一个单机上或许看不出什么问题,但在分布式计算、或者你需要提供jar供别人使用的时候,这个问题就会暴露。
我的另一篇文章:Java Serializable系列化与反系列化
凡是实现了Serializable的类,都必须实现serialVersionUID。而serialVersionUID的目的是为了版本升级的兼容性,当程序版本升级后可以通过反序列化维护对象的唯一性。
Eclipse可以为你自动生成serialVersionUID。