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,因此解序列化回来的对象还需要转换成原来的类型。
  • 静态变量不会被序列化,因为所有对象都是共享同一份静态变量值。