A、将序列化对象写入文件(被序列化的类得实现Serializable这个接口)
- 1.创建出FIleOutputStream
//如果文件不存在,它会自动被创建出来
FileOutputStream fileStream = new FileOutputStream("MySer.ser");
- 2.创建ObjectOutputStream
ObjectOutputStream os = new ObjectOutputStream(fileStream);
- 写入对象
//characterOne 是GameCharacter 类的引用。
os.writeObject(characterOne);
os.writeObject(characterTwo);
os.writeObject(characterThree);
- 关闭ObjectOutputStream
os.close();
B、解序列化(Deserialization):还原对象
- 1.创建出FIleInputStream
//如果文件不存在,就会抛出异常
FileInputStream fileStream = new FileInputStream("MySer.ser");
- 2.创建ObjectInputStream
ObjectInputStream os = new ObjectInputStream(fileStream);
- 写入对象
//每次调用readObject()都会从stream中读出下一个对象,读取顺序与写入顺序相同,次数超过则会抛出异常。
Object one = os.readObject();
Object two = os.readObject();
Object three = os.readObject();
- 转换对象类型
//返回值是Object类型,因此必须要转换类型 GameCharacter elf = (GameCharacter) one; GameCharacter troll = (GameCharacter) two; GameCharacter magician = (GameCharacter) three;
- 关闭ObjectInputStream
os.close();//FIleInputStream会自动跟着关掉
解序列化的时候发生了什么事:
1.对象从stream中读出来。
2.java虚拟机通过存储的信息判断出对象的class类型。
3.java虚拟机尝试寻找和加载对象的类。如果java虚拟机找不到或无法加载该类,则java虚拟机会抛出异常。
4.新的对象会被配置在堆上,但构造函数不会执行!很明显的,这样会把对象的状态抹去又变成全新的,而这肯定不是我们想要的结果,我们需要的是对象回到存储时的状态。
5.如果对象在继承数上有个不可序列化的祖先类,则该不可序列化类以及在它之上的类的构造函数(就算是可序列化的也一样)就会执行。一旦构造函数连锁启动后将无法停止。也就是说,从第一个不可序列化的父类开始,全部都会重新初始状态。
6.对象的实例变量会被还原成序列化时的状态值。transient变量会被赋值成null的对象引用或primitive主数据类型的默认为0、false等值。
总结(对象的序列化要点):
- 你可以通过序列化来存储对象的状态。
- 使用ObjectOutputStream来序列化对象(java.io)。
- Stream是连接串流或是链接用的串流。
- 连接串流是用来表示源或目的地、文件、网络套接字连接。
- 链接串流用来衔接连接串流。
- 用FileOutputStream链接ObjectOutputStream来将对象序列化到文件上。
- 调用ObjectOutputStream的writeObject(theObject)来将对象序列化,不需调用FileOutputStream的方法。
- 对象必须实现序列化这个接口才能被序列化。如果父类实现序列化,则子类也就自动地有实现,而不管是否有明确的声明。
- 当对象被序列化时,整个对象版图就会被序列化,这代表它的实例变量所引用的对象也会被序列化。
- 如果有不能序列化的对象,执行期间就会抛出异常。
- 除非该实例变量被标记为transient。否则,该变量在还原的时候会被赋予null或primitive主数据类型的默认值。
- 在解序列化时,所有的类都必须能让java虚拟机找到。
- 读取对象的顺序必须与写入的顺序相同。
- readObejct()的返回类型是Object,因此解序列化回来的对象还需要转换成原来的类型。
- 静态变量不会被序列化,因为所有对象都是共享同一份静态变量值。