【转】浅谈JAVA序列化机制
当我们需要把对象持久化,比如存入缓存或者网络传输时需要将对象进行序列化,序列化有很多种方式,本文主要介绍JAVA的序列化机制。
简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。
当实现java.io.Serializable接口的实体(类)没有显式地定义一个名为serialVersionUID,类型为long的变量时,Java序列化机制会根据编译的class自动生成一个serialVersionUID作序列化版本比较用,这种情况下,只有同一次编译生成的class才会生成相同的serialVersionUID 。
如果我们不希望通过编译来强制划分软件版本,即实现序列化接口的实体能够兼容先前版本,未作更改的类,就需要显式地定义一个名为serialVersionUID,类型为long的变量,不修改这个变量值的序列化实体都可以相互进行串行化和反串行化。
所谓Java的序列化是指,Java对象(注意是对象、对象)可以被编码成字节码的形式(主要用于存储或传输),同时可以进行逆向的操作,“反”序列化成JVM中的对象。
当然,不是所有的Java对象都可以序列化:必须实现java.io.Serializable接口。
把对象序列化做什么用呢?
一方面,在分布的Java平台之意传递信息(Java对象);另外一方面,实现深度克隆一个Java对象。
其实,JavaDoc里一开始就表达到非常基础且重要的问题,自己却一直没考虑到过的:如果继承一个未实现序列化接口的类,子类实现序列化接口,那么父类的属性(成员变量)哪些被序列化,哪些不被序列化呢?
protected, and (if accessible) package fields
细想想,很自然的事情,除了private的,都是在子类序列化范围内的。父类私有的对于子类也是不可见的,当然也就犯不上在序列化过程中包含它们。
另外,这里面还有一个很重要的限制:父类必须具备一下“无参数”的构建函数,以使得子类在(反)序列化过程中创建父类实例。
During deserialization, the fields of non-serializable classeswill be initialized using the public or protectedno-argconstructor of the class.我们都知道,java.io.Serializable接口是一个声明式的接口,没有接口方法是必须实现的。
但就像序列化ID(serialVersionUID)一样,在实现这个接口的同时,我们还是有很多与Java平台的“约定”可以做一些事情的。
比较直接可以想到,序列化和反序列化的过程,实际上也就是IO流“读写”过程而已。我们可以分别声明并实现两个特定的方法,参与到序列化和反序列化的过程中。
private void writeObject(java.io.ObjectOutputStream out) throws IOException private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;
write方法是序列化的过程,read方法是反序列化的过程。参数中的Stream(对象流)就是过程中,类对象的实际内容。
也就是说,Java平台会在序列化和反序列化的过程中分别调用这两个方法。
当然,我们一般也只是需要“额外”做一些事情,核心的内容还是希望保持缺省的机制,我们就有可以调用两个参数输入、输出流的defaultWriteObject方法和defaultReadObject方法。
上面这些内容,实际上用处还不是很大,至少我在开发过程中还没有什么需要一定要在这些机制上“做手脚”。
但在深入研究“单例”的问题时,遇到了下面这样的情况:单例。
我们都知道,单例是希望在一定范围(一个类加载器)内,对于某类的实例仅有一个唯一的对象实例。
实现单例模式的方法很多,这里不多说,唯独如果希望实现单例的类实现了序列化接口,会出现什么样的问题?
我把单例的实例,在同一个应用范围内,序列化,再反序列化回来,不是又得到一个该类的对象?这样不就不是单例了吗?
不用担心,序列化机制还提供了两个方法:
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException; ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
这两个方法可以理解为序列化和反序列化过程的入口和出口。writeReplace()返回的对象,就是要被序列化的对象,我们有机会在序列化前把这个对象给换成我们确定好的那个(如果不是“故意捣乱”,暂时没想到有什么用);而readResolve()方法就是在反序列化完成得到对象前,把这个对象给换成我们确定好的那个。
明白了吧?为了防止有人恶意通过序列化的机制破坏定义好的单例,我们就需要自己实现readResolve()方法,把单例定义的唯一实现在这个方法中返回。
原作者:龚勇

浙公网安备 33010602011771号