天天看点

深入了解java序列化

在日常开发中,前端与后端的交互,系统之间的远程调用都需要使用到序列化技术,在java中使用序列化非常简单,只需要将被序列化的对象的类实现Java.io.Serializable接口即可。

对于实现序列化接口的类,我们需要注意两点:

类中的静态变量我们是无法序列化的,因为序列化只是针对对象,而静态变量是类级别的。

当子类实现序列化接口,而父类没有实现序列化接口时,将子类进行序列化,再反序列化回来后,发现父类中的属性会被重新初始化,也就是说会调用父类的无参构造。如果没有无参构造,则会抛出异常。

下面是通过两个小案例来证明:

这段代码的运行结果为20,说明静态变量不会被序列化。

这段代码的运行结果为null,zi。验证了上面的第二点。

将对象序列化实际上调用的就是ObjectOutputStream的wirteObject方法,跟踪源代码可知能被序列化的对象除了是实现Serializable接口的对象,还可以是String,数组或者枚举对象:

再继续跟进writeOrdinaryObject方法,我们会发现会有这么一段逻辑:

先判断是不是实现了Externalizable接口(这个是Serializable的子接口,这里先不管,后面会解释),如果不是,则执行writeSerialData方法。跟进去:

我们发现源代码先判断了一下这个需要被序列化的类中有没有writeObject这个方法,如果有,那么就执行,如果没有,那么就执行默认的序列化方法。对于这个writeObject方法,有以下几个要求:

方法名必须叫writeObject

必须是私有的方法

返回值类型必须为void

只有满足了以上三点,在序列化对象的时候,才会执行我们自定义的序列化方法。当然,我们除了可以重写writeObject方法,我们还可以重写readObject,readObjectNoData,writeReplace,readResolve等方法,这些方法之间有什么联系,有什么作用呢?我们通过一段代码来探索一下:

执行结果如下:

根据执行结果我们我们可以知道,在对象序列化的时候,会先去执行被序列化的类中的writeReplace方法,再执行writeObject方法,如果重写了writeReplace方法,那么被序列化的对象就是这个方法的返回值,而writeObject方法主要作用就是在序列化前后可以做处理操作。

对应的读操作的方法分别是readResolve方法和readObject方法,将上面的代码再完善一下:

执行结果符合我们的预期:

下面我们再来解析一下上面提到的Externalizable接口,这个接口是Serializable接口的子接口,这个接口有两个抽象方法:writeExternal和readExternal,这个两个方法分别对应于writeObject和readObject,不同点在于writeObject和readObject都是私有方法,所以其子类不能复用并且不能复写,而writeExternal和readExternal是共有方法,其子类可以复用并且复写。