何为序列化?
序列化的目标是将对象保存到磁盘中,或允许在网络中直接传输对象。对象序列化机制允许把内存中Java对象转换成平台无关二进制流,从而允许把这种二进制流持久地保存在磁盘上,或者将这种二进制流传输到另一个网络节点,其他程序获得这种二进制流,都可以将二进制流恢复成原来的Java对象。序列化机制使得对象可以脱离程序的运行而独立存在。
所谓的对象序列化将一个Java对象写入IO流中转为字节流,反序列化则指从IO流中将字节流恢复该对象,注意静态常量不会被默认序列化。
序列化两种机制
Java中让某个对象支持序列化机制,必须让它的类是可序列化,为了让类是可序列化,则该类必须实现如下两接口之一
Serializable
Externalizable
所在可能在网络上传输的对象的类都应该是可序列化的,否则程序会出现异常。序列化是RMI必须实现的机制,而目前大部分程序都是分布式,需要跨平台,跨网络,所以通常建议:程序创建的每个JavaBean类都实现序列化接口。
实现Serializable接口
使用方式非常简单,目标类实现Serializable即可,无需实现任何方法。一旦实现Serializable接口,则该类的对象就是可序列化的。
Serializable是一个没有定义任何方法的标记接口,但是声明Serializable接口后保存读取对象就可以使用ObjectOutputStream,ObjectInputStream流了。ObjectOutputStream 是 OutputStream 的子类,但实现了 ObjectOutput 接口,ObjectOutput 是 DataOutput 的子接口,增加了一个 writeObject(Object obj) 方法将对象转化为字节写到流中,ObjectInputStream 是 InputStream 的子类,实现了ObjectInput 接口,ObjectInput 是 DataInput 的子接口,增加了一个 readObject() 方法从流中读取字节转为对象,序列化和反序列化的实质在于 ObjectOutputStream 的 writeObject 和 ObjectInputStream 的 readObject 方法实现。Serializable虽然是一个空接口,但是在调用writeObject方法时却充当了一种健全的校验作用。如果对象没有实现Serializable接口则在调用writeObject时抛出异常,Serializable算是一种接口标识机制。
自定义序列
有些时候对象中某些字段可能与内存位置,或者时间有关,序列化后没有太大实际意义,或者有些敏感信息(工资,银行密码)之类的内容不方便在网络传输默认序列化不合适,需要定制序列化。Java中提供关键字transient关键字,如果成员变量被transient修饰则不会被序列化。有些时候需要自定义序列方式,可以采用在类中自定义实现writeObject和readObject方法。
版本问题
默认情况下Java会根据类中信息自动生成一个版本号,如果反序列化时如果类发生了变化版本号就变化,也就与反序列化流中的版本号不匹配导致会抛出异常,所以我们为了更好的控制和性能问题会自定义 serialVersionUID 版本号来避免类定义发生变化后反序列化版本号不匹配异常问题,如果版本号一样时流中有该字段而类定义中没有则该字段会被忽略,如果类定义中有而流中没有则该字段会被设为默认值,如果对于同名的字段类型变了则会抛出 InvalidClassException。
虚拟机是否允许反序列化不仅取决于类路径和功能代码是否一致,还取决于另一个非常重要的点是两个类的序列化 ID 是否一致(就是 private static final long serialVersionUID = 1L)。