Java IO 对象流与序列化 Java编程思想读书笔记

来源:互联网 发布:高中历史辅助教材淘宝 编辑:程序博客网 时间:2024/06/05 11:27

在Java中,可以通过ObjectOutputStream写出/读取对象。写出对象 :

ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("employee.dat"));  Employee harry = new Employee("Harry Hacker",50000,1989,10,1);  Manager boss = new Manager("Carl Cracker",80000,1987,12,15);  out.writeObject(harry);  out.writeObject(boss);  

读取对象:

ObjectInputStream in = new ObjectInputStream(new FileInputStream("employee.dat"));  Employee e1 = (Employee)in.readObject();  Employee e2 = (Employee)in.readObject();  

但是,所有在对象流中存储或恢复的类都必须实现Serializable接口(Serializable接口没有任何方法)
class Employee implements Serializable{ ... }
注意:只有在写出对象时才用writeObect/readObject方法,对于基本类型值,需要使用如writeInt/readInt或writeDouble/readDouble这样的方法。(对象流类都实现了DataInput/Dataoutpt接口)
当对象中还有对象属性时,不能简单地保存和恢复对象属性的地址,因为当对象被重新加载时,它可能占据与原来完成不同的内存址。与此不同的是,每个对象都用一个序列号保存,这就是这种机制被称为对象序列化的原因。其具体算法为:
每一个对象引用都关联一个序列号
对于每个对象,当第一次保存时,写出其对象数据到流中
对于某个之前已经被保存过的对象,那么只写出“与之前保存的序列号为x的对象相同”标记
在读回对象时,整个过程反过来:
对于流中的对象,在第一次读取其序列号是地,构建它,并使用流中的数据来初始化它,然后记录这个序列号与构建的对象之间的关联

在读取到“与之前保存过的序列号为x的对象相同”标记,获取与这个序列号相关联的对象引用


注意:序列化的另一种非常重要的应用是通过网络将对象集合传送到另一台计算机上。而保存原生内存地址的做法,对于不同的处理器之间的通信也是毫意义的。但保存序列号允许将对象集合从一台机器传送到另一台机器。

可以将不需要序列化的属性域标记为transient,防止它们被序列化。

注意:从不同IO流从同一文件中读取的对象并不相同,在涉及单例时应注意。

序列化版本管理 

在程序演化过程中,类的内容也会不断发生变化,序列化读取旧版本的对象时就会引起问题。为了与早期版本保持兼容,可以在类中加入它的版本号:

private static final long serialVersionUID = -8421069283903896412L;

那么这个类的所有较新版本都必须把serialVersionUID常量定义为与最初版本的版本号一致,否则对象流拒绝读入具有不同版本号的对象。

一个类仅有方法发生变化时,在读入新对象数据时才不会有任何问题。但是,如果数据域产生了变化,那么就可能有问题。如,旧文件对象可能比程序中的对象具有更多或更少的数据域,或者数据域的类型可能有所不同。在这些情况下,对象流将尽力将流对象转换为这个类当前的版本。

对象流会将这个类当前版本的数据域与流中版本的数据域进行比较,当然,对象流只会考虑非瞬时和非静态的数据域。如果这两个部分数据域之间名字匹配而类型不匹配,那么对象流不会尝试将一种类型转制成另一种类型,因为这两个对象不兼容;如果流中的对象具有当前版本中所没有的数据域,那么对象流会忽略这些额外的数据,如果当前版本具有在流对象中所没有的数据域,那么这些新添加的域将被设置成它们的默认值(如果是对象则设置为null,如果是数字则为0,如果是boolean值则是false)。

如employee版本1如下 :

public class Employee implements Serializable{      private static final long serialVersionUID = -8421069283903896412L;            private String name;      public Employee(String n){          this.name = n;      }  }  


然后我们使用输出流将一个对象保存到对象中:

Employee em = new Employee("bin");    out = new ObjectOutputStream(new FileOutputStream("employeeWithVersion.dat"));  out.writeObject(em);  

employee版本升级,添加了数据域salary:

public class Employee implements Serializable{      private static final long serialVersionUID = -8421069283903896412L;            private String name;      private double salary;      public Employee(String n,double s){          this.name = n;          this.salary = s;      }  }  

但我们仍然可以使用输入流将开始保存的版本1对象读出,因为我们使用了版本号:

in = new ObjectInputStream(new FileInputStream("employeeWithVersion.dat"));  Employee readEm = (Employee) in.readObject();  




0 0
原创粉丝点击