Serializable接口
Serializable接口只是一个标记接口,不包含任何的方法。
Java的对象序列化是指将那些实现Serializable接口的对象转化成一个字节序列,并能够用这个字节序列完全恢复为原来的对象。这种序列化机制能自动弥补不同操作系统之间的差异。
序列化
对象转成字节
- 创建某种OutputStream,例FileOutputStream、ByteArrayOutputStream等。
- 用ObjectOutputStream封装使用的OutputStream。
- 调用writeObject()进行对象序列化。
反序列化
字节转对象
- 创建某种InputStream,例FileInputstream、ByteArrayInputStream等。
- 用ObjectInputStream封装使用的InputStream。
- readObject()进行反序列化。
对象的序列化是基于字节的,不能使用Reader和Writer等基于字符的层次结构。
在进行反序列的时候,Serializable对象完全以它存储的二进制位作为基础来构造,而不调用构造器(构造方法)。
序列化前的对象与反序列还原后对象的关系
属于深度复制
- 浅复制:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。
- 深复制:被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的所引用对象的对象都复制了一遍。
序列化前的对象与反序列还原后对象的地址不一样,而两个对象中的引用也不一样,但是值都一样。换句话说,通过序列化操作,我们可以实现对任何Serializable对象的”深度复制(deep copy)”——这意味着我们复制的是整个对象网,而不仅仅是基本对象及其引用。
对于用同一个IO流进行序列化操作的Serializable对象,他们的地址是一样的,同一对象,与用其他IO流的Serializable对象地址却不相同。
只要将对象序列化到单一流中,就可以恢复出原来对象一样的对象网,而且只要在同一流中,对象都是同一个。
serialVersionUID
UID:stream unique identifier
在Java中SerialVersionUID是唯一控制着能否反序列化成功的标志,只要这个值不一样,就无法反序列化。
向上兼容性
旧版本的Serializable对象能读取新版本的Serializable对象序列化后的数据流(二进制)。
- 新数据流中的多余的内容将会被忽略;
- 但是在新版本的Serializble对象中修改了老版本的Serializable对象的内容,即使UID不变,也会报异常。
向下兼容性
新版本的Serializable对象能读取旧版本的Serializable对象序列化后的数据流(二进制)。
- 旧的数据流中包含的所包含的所有内容将会被恢复,新版本的类中没有涉及到的部分将保持默认值。
- 对于那些没有默认值进行初始化的参数,需要进行初始化操作。
private void readObject(java.io.ObjectInputStream in) throws IOException,ClassNotFoundException{
in.defaultReadObject();//先反序列化对象
if(version=123){
//初始化其他字段
}else if(version=456){
//初始化其他字段
}else{
//太老的版本不支持
throw new InvalidClassException();
}
}
要保证in.defaultReadObject()的执行,要求serialVersionUID保持一致,所以version不能使用serialVersionUID,version是我们预先设置的,final long version=xxxx。
保持向下兼容性的要求
1. serialVersionUID保持一致。
2. 预先设置好我们自己的版本识别标记的final long version=xxxx。
3. 保证对象的所有字段都得到了初始化。
serialVersionUID做为序列化的版本控制是一个非常有用的兼容手段,通常情况下,我们应该手工设置该值,idea或eclipse等IDE会有提示你设置其值。serialVersionUID可以任意设置,根据不同的兼容性做相应改动。