一、概述
有一个Object持久化于缓存中,经常需要变更字段(添加或删除),每次做变更就要更改缓存表(担心不兼容带来问题,一直不确定哪些变更会来问题或引起什么样的问题),我希望实现一种序列化,当变更或删除字段时不需要变更缓存表,这需要达到两个目的:1、新的类访问旧的缓存时没问题;2.旧的类访问新的缓存时也没问题。
二、Java序列化
java序列化提供了一个框架,用来将对象编码成字节流,并从字节流编码中重新构建的对象。将对象编码为字节流称之为序列化,反之将字节流重建成对象称之为反序列化。
三、序列化实现方式
简单示例
一个Person类,具有两个属性:name和age;
public class Personimplements Serializable {
private Stringname ;// 姓名
private int age ;// 年龄
public Person(String name,int age){// 通过构造方法赋值
name = name ;
age = age ;
}
//getter ,setter方法
}
生成一个Person的实例p,将期通过ObjectOutputStream写入文件,并通过ObjectInputStream读出来。这是一个完整的序列化/反序列化过程:ObjectOutputStream将p转化成字节流写入文件,ObjectInputStream将从文件中读出的字节流重新创建newPerson实例。
@Test
public void testSerializable()throws Exception {
File file =new File("p.dat");
Person p =new Person("xiaoming",10);
ObjectOutputStream oos =new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(p);
oos.close();
ObjectInputStream ois =new ObjectInputStream(new FileInputStream(file));
Object newPerson = ois.readObject();
ois.close();
System.out.println(newPerson);
}
通过上面的过程,我们可以看出默认的序列化机制对使用者而言是非常简单的。序列化具体的实现是由ObjectOutputStream完成的;反序列化的具体实现是由ObjectInputStream完成的。
四、为何要实现Serializable
最重要的两个原因是: 1、将对象的状态保存在存储媒体中以便可以在以后重新创建出完全相同的副本; 2、按值将对象从一个应用程序域发送至另一个应用程序域。 通俗的说:在分布式应用中,你就得实现序列化,如果你不需要分布式应用,那就没那个必要实现序列化。
Serializable是标识类
Serializable是一个空接口,没有什么具体内容,它的目的只是简单的标识一个类的对象可以被序列化。
五、哪些情况下需要序列化
当你想把的内存中的对象写入到硬盘的时候;
当你想用套接字在网络上传送对象的时候;
当你想通过RMI传输对象的时候;再稍微解释一下:
比如说你的内存不够用了,那计算机就要将内存里面的一部分对象暂时的保存到硬盘中,等到要用的时候再读入到内存中,硬盘的那部分存储空间就是所谓的虚拟内存。在比如过你要将某个特定的对象保存到文件中,我隔几天在把它拿出来用,那么这时候就要实现Serializable接口;
在进行java的Socket编程的时候,你有时候可能要传输某一类的对象,那么也就要实现Serializable接口;最常见的你传输一个字符串,它是JDK里面的类,也实现了Serializable接口,所以可以在网络上传输。
如果要通过远程的方法调用(RMI)去调用一个远程对象的方法,如在计算机A中调用另一台计算机B的对象的方法,那么你需要通过JNDI服务获取计算机B目标对象的引用,将对象从B传送到A,就需要实现序列化接口。
六、底层实现原理
先看下面代码
Foo myFoo = new Foo();
myFoo .setWidth(37);
myFoo.setHeight(70);
当通过下面的代码序列化之后,MyFoo对象中的width和Height实例变量的值(37,70)都被保存到foo.ser文件中,这样以后又可以把它从文件中读出来,重新在堆中创建原来的对象。当然保存时候不仅仅是保存对象的实例变量的值,JVM还要保存一些小量信息,比如类的类型等以便恢复原来的对象。
FileOutputStream fs = new FileOutputStream("foo.ser");
ObjectOutputStream os = new ObjectOutputStream(fs);
os.writeObject(myFoo);
那么大概步骤如下所示
1.Make a FileOutputStream
`FileOutputStream fs = new FileOutputStream("foo.ser"); `
2.Make a ObjectOutputStream
`ObjectOutputStream os = new ObjectOutputStream(fs); `
3.write the object
`os.writeObject(myObject1);
os.writeObject(myObject2);
os.writeObject(myObject3); `
4.close the ObjectOutputStream
`os.close();`
serialVersionUID适用于Java的序列化机制。
简单来说,Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException。
serialVersionUID有两种显示的生成方式:
一是默认的1L,比如:private static final long serialVersionUID = 1L;
二是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,比如:private static final long serialVersionUID = xxxxL;