浅谈序列化与反序列化

什么是序列化?

序列化(serialization)在计算机科学的数据处理中,是指将数据结构或对象状态转换成可取用格式(例如存成文件,存于缓冲,或经由网络中发送),以留待后续在相同或另一台计算机环境中,能恢复原先状态的过程。依照序列化格式重新获取字节的结果时,可以利用它来产生与原始对象相同语义的副本。对于许多对象,像是使用大量引用的复杂对象,这种序列化重建的过程并不容易。面向对象中的对象序列化,并不概括之前原始对象所关系的函数。这种过程也称为对象编组(marshalling)。从一系列字节提取数据结构的反向操作,是反序列化(也称为解编组、deserialization、unmarshalling)。 《维基百科》

粗略的说,我们可以将对象在内存中持有的属性称之为对象的状态,那么序列化的作用就就是保存对象的状态,类似于存档。像下面这样:

image.png

我们将学生对象写入到磁盘、内存、或者网络中,这个学生对象只拥有两个属性: age和name。然后JVM在将该对象从内存或磁盘或网络中加载进去,age还是20,name还是zs。

为什么要引入序列化?

java引入序列化主要是为了支持两种特性,一是 Java 的远程方法调用(Remote Method Invocation,RMI),它使存活于其他计算机上的对象使用起来就像是存活于本机上一样。当向远程对象发送消息时,需要通过对象序列化来传输参数和返回值。

再者,对 Java Beans 来说,对象的序列化也是必需的(在撰写本文时被视为失败的技术),使用一个 Bean 时,一般情况下是在设计阶段对它的状态信息进行配置。这种状态信息必须保存下来,并在程序启动时进行后期恢复,这种具体工作就是由对象序列化完成的。---《java编程思想》第四版

补充介绍内容:

谈谈我对java bean的序列化是一种失败的技术的理解:

这也许再说的是java默认的序列化机制,java默认的序列化机制有以下缺点:

  • 无法跨语言 只能被java语言自身所使用。
  • 码流太大 也就是说序列化出来的文件比较大,这会比较占用网络带宽。
  • 性能比较差 这也是有其他序列化机制出现的原因(后文会对常见的序列化机制做一个简单的介绍)

再有就是反序列化的时候是递归操作,解释一下,假设我们有这些类叫A、B、C、D。B是A的成员变量,C是B的成员变量,D是C的成员变量。我们反序列化A的时候,JVM会从最下面的一层去反序列化。众所周知,递归是和栈挂钩的,然后你引用关系比较深的话,栈就会承受不住,也就是stackOverflow。所以你反序列化的时候,引用关系就不要太深。

似乎在大数据领域会感受比较密切,参看这个回答:

你什么时候对 Java 感到绝望?

为什么序列化需要实现Serializable接口?

用jdk提供的序列化,对应的类需要实现Serializable接口。如果你不用jdk提供的序列化,不实现Serializable接口也行。
Serializable接口属于标记接口,里面啥也没有,只是起一个标记作用,相当于一个标签。

刚开始我的想法是这个接口是用来判断哪些是需要序列化的,哪些是不需要序列化的,通过instanceof判断,如果实现了Serializable就是说明需要进行序列化的,没实现的就是不要进行序列化的。但是后来我又想,这不是应该由开发者来决定吗?
开发者觉得这个对象需要序列化了,就调用对应的方法将对象序列化,不需要的时候肯定不会调用啊。

稍微牵强一点的解释是,序列化接口和serialVersionUID(后文会介绍这个字段的作用)字段关联在一起,形成一种约定。
image.png

你看在调用writeObject0方法时,就会校验序列化对象有没有实现Serializable接口,如果该类没有实现,就序列化不了。

默认序列化需要注意的点

每个实现了Serializable接口的类还有一个与之相关联的版本号,我们称之为serialVersionUID,他的作用是验证发送方发送的对象和接收方接收的对象是否是相容的,也就是看serialVersionUID是不是相等,如果不相等,那么反序列化就会失败。

一般来说,常用的IDE在类实现Serializable接口后,就会给一个警告,要求产生一个serialVersionUID,因为就算你不给,编译器根据java平台规范在运行时也会生成serialVersionUID,serialVersionUID的值和属性是有关系的,增加或减少一个属性都会引起serialVersionUID的变化。这也是官方建议要序列化的类(默认是使用jdk提供的序列化机制),务必给一个版本号,这是为了向前兼容,假设你不给,又增加了或减少了属性,序列化版本号就发生了变化,你反序列化之前的类,就会失败。
这也是阿里巴巴代码规约强调的:

序列化类新增属性时,勿修改 serialVersionUID 字段,否则会导致反序列化失败

常见的序列化框架简介

  • protostuff
google出品,跨语言,跨平台,性能强大。
  • FastJson
阿里出品,性能也比较强大,仅能在java平台使用,所支持的序列化也就是将json转成对象,对象转成json。
同类型的也有jackson,Gson,都能将JSON转成对象。
  • Hessian
粗略的说Hessian 是一个轻量级的RPC框架,也提供序列化功能。

总结一下

  • 什么是序列化?
将数据结构或对象状态转为可取用格式(txt、JSON) 。
  • 为什么需要序列化,或者说引入序列化机制?
宏观上来说,是为了暂存对象的状态,为了传递对象,比如说RMI。
  • 是不是序列化就一定需要实现Serializable接口?
不一定,需要看你调用对应序列化机制的协定。

参考资料:

你可能感兴趣的