前言
在Java中,我们知道可以随意创建对象,只要对象未被GC回收,我们都可以继续在程序里使用,但这些对象只是存在于JVM内存中的,我们JVM一旦停止,这些对象就消失不见了。
经常有些时候,我们需要把这些对象持久化下来,再次需要时,再重新把对象读取出来,Java中有一种机制,对象序列化机制(object serialization)便可以帮我们完成相关功能。
对象序列化,可以方便的把对象状态保存为字节数组,可以通过字节流进行远程网络传输等,接收到字节流,通过反序列化机制,可以将字节数组转换为相关对象。
常说的RPC远程调用,相关传输对象的生成类就必须实现序列化以便在网络间传输。
正文
Serializable接口
在Java中,我们最常用的实现序列化和反序列化的方法就是相关类实现 java.io.Serializable 接口了,这也是Java给我们提供的一个方便的API。
我们创建一个Apple类,实现序列化接口,通过测试,可以看到相关对象生成的字节码和反序列化后的对象。
1 | public class Apple implements Serializable { |
1 | public class Test { |
测试输出结果:
当我们去掉Apple类的Serializable接口后,执行测试会抛出异常,说明对象无法被序列化。
序列化ID(serialVersionUID)
JVM虚拟机是否可以对某个对象进行反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的点是两个类的序列化ID是否一致(就是 private static final long serialVersionUID)。
序列化ID有两种生成策略,一个是固定的 1L,一个是随机生成一个不重复的 long 类型数据(实际上是使用 JDK 工具生成),在这里有一个建议,如果没有特殊需求,就是用默认的 1L 就可以,这样可以确保代码一致时反序列化成功。那么随机生成的序列化ID有什么作用呢,有些时候,通过改变序列化 ID 可以用来限制某些用户的使用。
序列化的实现方式
在Java中,我们还可以利用其它方式对对象进行序列化,我总结了几种序列化方式如下。
让我们一起来看一下:
我们提供一个序列化与反序列化通用接口
1 | public interface Serializer { |
标准的Java序列化
1 | public class JavaSerializer implements Serializer { |
可以看到序列化就是我们将对象通过ObjectOutputStream转化为ByteArrayOutputStream字节流,反序列化就是将字节流转换为对象流并读取。
FST实现序列化
需要引入相关jar包
1 | <dependency> |
1 | public class FSTSerializer implements Serializer { |
Kryo实现序列化
需要引入相关jar包
1 | <dependency> |
1 | public class KryoSerializer implements Serializer { |
KryoPool实现序列化
由于kryo创建的代价相对较高,我们可以使用一个KryoPool池来管理Kryo,使用空间换取时间,提高运行效率。
我们使用一个双端队列来对Kryo进行管理,相关代码如下:
1 | public class KryoPoolSerializer implements Serializer{ |
Jackson 实现序列化和反序列化
Jackson也可以实现相关序列化和反序列化功能,需要引入jackson 的jar包。
使用writeValueAsBytes和readValue方法即可完成相关功能。
1 | <dependency> |
代码如下:
1 | public class JacksonSerializer implements Serializer{ |
FastJson实现序列化和反序列化
FastJson实现序列化与反序列化,需要引入相关jar包,如下:
1 | <dependency> |
相关代码如下:
1 | public class FastJsonSerializer implements Serializer{ |
以上的序列化与反序列化的对象都需要实现Serializable接口。
我们对上述代码进行相关测试:
1 | public class SerializationUtils { |
可以看到输出后的结果:
根据结果判断正确性后,也大致能看出各种序列化方式的一些优点和缺点。
JavaSerializer 明显的优点是不用引用包,也是Java程序默认的序列化方式,但是其序列化后占用空间是几种序列化方式里最大的,如果遇到大对象序列化,处理起来可能就比较力不从心了。
FSTSerializer、JacksonSerializer、FastJsonSerializer 它们是一种比较适中的序列化方式,序列化后的字节比Java方法少,时间也差不多。
KryoSerializer 是一种比较优异的序列化方式,可以看到它的序列化后的字节很短,占用空间少,且序列化和反序列化时间短。
KryoPoolSerializer 这种相当于KryoSerializer的改进版,利用了一部分内存空间,进一步降低了序列化和反序列化的时间。
正因为Kryo如此高效的序列化和反序列化性能,因此在大数据领域应用广泛。如Apache的spark、hive等。
如果需要更准确的结果比较各种序列化方式的性能,可以创建大量对象并对它们进行序列化记录时间等参数比较,这儿就不过多讨论了。
结语
通过对序列化和反序列化的简单介绍,并比较了一些常用的序列化方式,我们对对象的序列化与反序列化有了更进一步的认知。