Java 序列化

Java序列化技术的基础是被序列化的类需要实现 Serializable 接口,使用 ObjectInputStream 和 ObjectOutputStream 进行对象的读写。

默认的序列化机制

如果仅仅只是让某个类实现Serializable接口,而没有其它任何处理的话,则就是使用默认序列化机制。使用默认机制,在序列化对象时,不仅会序列化当前对象本身,还会对该对象引用的其它对象也进行序列化,同样地,这些其它对象引用的另外对象也将被序列化,以此类推。所以,如果一个对象包含的成员变量是容器类对象,而这些容器所含有的元素也是容器类对象,那么这个序列化的过程就会较复杂,开销也较大。

序列化ID

虚拟机是否允许反序列化,不仅取决于类路径功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致(就是 private static final long serialVersionUID = 1L)。

序列化ID生成有两种策略:一种是固定的 1L,另一种是随机生成一个不重复的long类型数据。服务端随机生成的序列化 ID 的作用是,有些时候,通过改变序列化 ID 可以用来限制某些客户端的使用(版本更新)。

静态变量序列化

序列化不保存静态变量。

父类的序列化和Transient关键字

一个子类实现了 Serializable 接口,要想将父类对象也序列化,就需要让父类也实现 Serializable接口。如果父类不实现的话,就需要有默认的无参构造函数

在父类没有实现 Serializable接口时,虚拟机不会序列化父对象,而一个 Java 对象的构造必须先有父对象,才有子对象,反序列化也不例外。所以反序列化时,为了构造父对象,只能调用父类的无参构造函数作为默认的父对象。

Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。

敏感字段加密

在序列化过程中,虚拟机会试图调用对象类里的 writeObject 和 readObject 方法,进行用户自定义的序列化和反序列化,如果没有这样的方法,则默认调用是 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法。用户自定义的 writeObject 和 readObject 方法可以允许用户控制序列化的过程,比如可以在序列化的过程中动态改变序列化的数值。基于这个原理,可以在实际应用中得到使用,用于敏感字段的加密工作。

序列化存储规则

Java 序列化机制为了节省磁盘空间,具有特定的存储规则,当写入文件的为同一对象时,并不会再将对象的内容进行存储,而只是再次存储一份引用,增加的字节存储空间就是新增引用和一些控制信息的空间。反序列化时,恢复引用关系,多个引用指向唯一的对象,二者相等。存储规则极大的节省了存储空间。

将一对象两次进行序列化保存时,写入一次后修改对象属性值再次保存第二次,这种情况下,虚拟机根据引用关系知道有一个相同对象已经写入到文件中,因此只保存第二次写的引用。所以读取时,都是第一次保存的对象。

Externalizable接口

无论是使用transient关键字,还是使用writeObject()readObject()方法,其实都是基于Serializable接口的序列化。JDK中提供了另一个序列化接口–Externalizable,使用该接口之后,之前基于 Serializable 接口的序列化机制就将失效。

Externalizable继承于Serializable,当使用该接口时,序列化的细节需要由程序员去完成。如果writeExternal()readExternal()方法未作任何处理,那么该序列化行为将不会保存/读取任何一个字段。

另外,若使用Externalizable进行序列化,当读取对象时,会调用被序列化类的无参构造器去(而且是public修饰)创建一个新的对象,然后再将被保存对象的字段的值分别填充到新对象中。(Serializable不会调用任何构造方法,而是直接还原对象)

readResovle()序列化时的对象替换

在有些情况下,可能会希望在序列化的时候使用另外一个对象来代替当前对象。

其中的动机可能是当前对象中包含了一些不希望被序列化的域,比如这些域都是从另外一个域派生而来的;也可能是希望隐藏实际的类层次结构;还有可能是添加自定义的对象管理逻辑,如保证某个类在JVM中只有一个实例。
相对于把无关的域都设成transient来说,使用对象替换是一个更好的选择,提供了更多的灵活性。


感谢:
https://www.ibm.com/developerworks/cn/java/j-lo-serial/#ibm-pcon
http://www.blogjava.net/jiangshachina/archive/2012/02/13/369898.html
http://www.infoq.com/cn/articles/cf-java-object-serialization-rmi