序列化及Java Serializable序列化接口

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m0_37793798/article/details/83661959

2018.11.02

前言

某项目新版本上线,新版本中添加了A类,而A类最终会通过ObjectOutputStream#writeObject方法1序列化到HDFS中。而在上线完成之后才发现实现了Serializable序列化接口的A类并没有定义serialVersionUID,换言之,如果下次上线的版本A类发生了修改,就很有可能会导致serialVersionUID改变,进而反序列化就会失败。后果很重要,闻者都为虎躯一震。

Hello,序列化

序列化的定义就是将对象转换成字节流的过程1。那问题来了,对象好好的,为什么要转换成字节流呢?

对象  ---------------------------------------------发送方
   `
     ` (序列化)
       `
         `一串字节流 --> 网络|磁盘...  --------------中间介质
                           `
                             `(反序列化)
                               `
                                 `
                                 对象  -------------接收方

首先对象它是存在于它所在机器的内存里,一旦断电或者程序运行结束,它就会消失。那一旦我们想要持久化这个对象或者通过网络传输2,该怎么办呢?要回答这个问题,我们需要知道,计算机的世界只有0/1,不管你是对象也好文件也罢,最终的形态它都是一串字节流。同一段字节流,在JVM里,它是个对象;但放到C++的运行时里,它还会是个对象吗,那就未必了。所以,如果当我们要在A环境中要将对象传输到B环境,无论我们是想要存储还是仅仅是传输,传输过去的内容都只能是字节流。

然后是怎么序列化的问题。对象,我可以把它序列化成000011001,我也可以序列化成1000101101…01。那么到底该怎么序列化,才能让接收它的人或者读取它的人,能把它正确地还原回来(反序列化)。这就取决于序列化方法的实现。序列化方法其实就是密钥,你用它来加密解密,那么怎么定义这个密钥,视情况而定。在一般情况下,一个没有自定义序列化方法的可序列化类,它的实例序列化出来的结果会把它所有的成员都包含进去,那么反序列化出来的对象自然就是一个完整的实例。但如果考虑到效率、空间大小、实际需要等因素,这个类并不到所有成员都为接收它的人所需要,那么就可以自定义序列化方法,将需要的内容进行序列化;而接收它的人,就只要按相应地规则将其反序列化出来即可。

Serializable:不能忽视的你

上节中,我们已经知道序列化方法决定了如何转换。但是不是所有字节流都值得或者能够转换呢?给你一个A类,你把它序列化并存储到磁盘文件;过段时间,A类变了,添加了一个新的成员,这时你还能不能用新的A类序列化方法将原本存储在文件里的A实例反序列化出来呢?这个答案在理论上是可行的,但一个前提是你根据两个版本A类的差异,自定义了A类的反序列化方法,另一个前提后面会讲解。

一旦A类没有自定义序列化/反序列化方法,那么作为JVM,这种情况是不能允许文件里的对象反序列化成新A类的,因为它不符合新A类的定义。那运行时环境是如何保证这点的呢?答案就是第一节前言所提到的serialVersionUID

Java对每一个可序列化的类都会关联上一个serialVersionUID序列版本id,用于标识这个类。类在没有定义serialVersionUID的情况下,JVM会为它生成一个UID作为它的序列版本id,引用某书籍的表述3

Joshua Bloch says in Effective Java that the automatically generated UID is generated based on a class name, implemented interfaces, and all public and protected members. Changing any of these in any way will change the serialVersionUID.

扫描二维码关注公众号,回复: 4196675 查看本文章

也就是说,默认生成的serialVersionUID是会根据这个类的类名、公共成员等信息得到一个特定的哈希值。而在前言讲的case里,因为没有定义serialVersionUID,所以一旦A类发生改动,那么原本存储到HDFS的数据,就将无法读取,对于生产环境来说,这样的问题是灾难性的。

所以,Java Serializable接口在使用上非常重要的一点就是:请务必显示地为可序列化类添加一个serialVersionUID序列版本id。语法如下:

private static final long serialVersionUID = -1L;

  1. Serialization by Microsoft ↩︎ ↩︎

  2. What’s this “serialization” thing all about? ↩︎

  3. How to generate serialVersionUID ↩︎

猜你喜欢

转载自blog.csdn.net/m0_37793798/article/details/83661959