1 将序列化对象写入文件
一般流程:
//可能会产生异常
try {
//打开文件
FileOutputStream fileStream=new FileOutputStream("MyGame.ser");
//创建对象输出流
ObjectOutputStream oStream=new ObjectOutputStream(fileStream);
//写入对象
oStream.writeObject(new Integer(1));
//关闭对象输出流
oStream.close();
//关闭文件流
fileStream.close();
}catch (Exception e) {
e.printStackTrace();// : handle exception
}
- 对象存储了什么?
在存储对象时,我们想的是恢复对象后能和存储时有一样的状态。但是如果在对象中有其它对象成员呢,我们知道在对象中保存的实际是其它对象的引用,真正的对象在堆上其它地方。如果只保存此对象的引用,显然在恢复时会出错误。
序列化程序会将对象版图上的所有东西存储起来。被对象的实例变量所引用的所有对象都会被序列化。
例如:zoo对象中有Lion[] lionArray对象,lionArray对象中又有多个lion,那么在存储zoo时,lionArray和其中的lion都会被序列化存储。
- 可序列化接口Serializable与不可序列化关键词transient
如果要让自己设计的类可以被序列化存储,就实现Serializable接口,不需要实现任何方法。实现了此接口的类,其子类也可以序列化。
如果要让一个类可序列化,那么此类中的所有成员都应该是可序列化的,即整个对象版图都必须正确地序列化,不然就全部失败。
如果某实例变量不能或不应该被序列化,就把它标记为transient(瞬时)的。
某类可序列化但其父类不可序列化的情况:
当对象被还原且它的父类不可序列化时,父类的构造函数会跟创建新的对象一样执行。
有多个引用指向同一个对象的情况:
只有一个对象会被存储,恢复后原引用仍指向同一对象。
如果某个实例被标记为transient的:
那么在恢复时会默认设为null,如果该值很重要,可通过其它方法保存其值,待恢复后再将值赋给它。
2 解序列化:还原对象
一般流程:
try {
//打开文件
FileInputStream fileStream=new FileInputStream("MyGame.ser");
//创建对象输入流
ObjectInputStream iStream=new ObjectInputStream(fileStream);
//读取对象
Object oneObject=iStream.readObject();
//转换对象
Integer aInteger=(Integer)oneObject;
//关闭对象输入流
iStream.close();
//关闭文件输入流
fileStream.close();
} catch (Exception e) {
e.printStackTrace();// TODO: handle exception
}
- 对象恢复时发生了什么
在恢复对象时,java虚拟机首先通过存储信息判断对象的class类型,如果无法加载该类型则抛出异常。
新的对象被配置在堆上,但其构造函数不会执行,如果其某个父类是不可序列化的,则该父类及其之上的父类的构造函数会执行。
未被标记为transient的变量会恢复成存储时的内容,被标记为transient的变量会被设置为0、false或null。
静态变量不会被序列化。
3 版本控制
如果对象被保存后,它原来的类被修改了,这样在读取对象时就有可能会出错。例如原来类中的某个变量被删除了,或者类型被修改等等。有些修改会损害解序列化,有些不会。
会损害解序列化的修改 | 通常不会有事的修改 |
---|---|
删除实例变量 | 加入新的实例变量(会使用默认值) |
改变实例变量的类型 | 在继承层次中加入新的类 |
将非瞬时的实例变量改为瞬时的 | 从继承层次中删除类 |
改变类的继承层次 | 不会影响解序列化程序设定变量值的存取层次修改 |
将类从可序列化改成不可序列化的 | 将实例变量从瞬时改成非瞬时(会使用默认值) |
将实例变量改成静态的 |
每当对象被序列化的同时,该对象都会被加上一个类的版本识别ID,被称为serialVersionUID,在还原对象时,会对比对象与java虚拟机上类的serialVersionUID,只有相同才会判定为相同的类。
在修改了类之后通常修改了类之后其serialVersionUID会发生改变,也就是还原对象时会找不到其类型。有一个解决方法是将serialVersionUID放在类中,使其固定。
使用serialver工具可以查询某个类的serialVersionUID,再将其赋给类中的static final long serialVersionUID变量
public class Dog{
static final long serialVersionUID=-3480159834908509L;
//其它代码
}