当你需要存储相同类型的数据时,使用固定长度的记录格式是一个不错的选择。但,在OOP中创建的对象很少全部都具有相同的类型。
例如,你可能有一个称为staff(见下面demo)的array,它名义上是一个Employee记录数组,但是实际上却包含诸如Manager这样的子类实例。
为解决这个问题,当然可以设计出一种数据格式来存储这种多态集合,但由于有序列化,设计这种类型是没有必要的。
Java语言支持一种称为对象序列化(Object serialization)的非常通用的机制,它可以将任何对象写出到流中,并在之后将其读回。
如果需要对象流中存储或恢复的所有类都进行一下修改,这些类必须实现Serializable接口;
Serializable接口没有任何方法,与Cloneable接口很相似。
只有在写出对象时才能用writeObject/readObject方法,对于基本类型值,需要使用诸如writeInt/readInt或writeDouble/readDouble这样的方法。对象流类都实现了DataInput/DataOutput接口。
由于对象引用而形成的对象网络,Object Serialization不能去保存和恢复对象的地址,因为当对象被重新加载时,JVM为对象分配的内存地址与在另一个JVM中分配的不同。
Object Serialization在处理的办法是使用序列号(serial number):
每个对象都是用一个序列号保存。由于序列化使用序列号代码内存地址,所以允许将对象集合从一台机器传送到另一台机器。正是由于序列号的使用,这种机制被称为对象序列化。
其算法如下:
(1)对遇到的每一个对象引用都关联一个序列号
(2)对于每个对象,当第一次遇到时,保存其对象数据到流中
(3)如果某个对象之前已经被保存过,那么只写出“与之前保存过的序列号为x的对象相同”;在读回对象时,整个过程是反过来的
(4)对于流中的对象,在第一次遇到其序列号时,构建它,并使用流中数据来初始化它,然后记录这个顺序号和新对象之间的关联
(5)当遇到“与之前保存过的序列号为x的对象相同”标记时,获取与这从此顺序号相关联的对象引用
例外的处理:
某些数据域永远都不应该被序列化,例如,只对本地访问有意义的存储文件句柄或窗口句柄的整数值,这种信息在稍后重新加载对象或将其传送到其他机器上时都是没有用处的。事实上,这种域的值如果不恰当,还会引起本地方法崩溃。
Java有一种很简单的机制来防止这种域被序列化,那就是将这些域使用transient关键字来修饰。如果这些域属于不可序列化的类,使用transient关键字修饰即可。瞬时的域在对象被序列化时问题被跳过的。
例如java.awt.geom包中有大量类都是不可能序列化的,例如Point2D.Double,在对象定义使用private transient Point2D.Double point;这样就可以避免NotSerializableException;
在序列化和反序列化对象被认为是惟一时,需要对枚举值做特殊处理。唯一的对象,譬如单例和类型安全的枚举
protected Object readResolve() { if (value==1) { return Orientation.HORIZONTAL; }else if (value==2) { return Orientation.VERITAL; } return null; }
package io.serial; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Date; import java.util.GregorianCalendar; /*2015-7-9*/ public class ObjectStreamTest { public static void main(String[] args) { Employee harry = new Employee("Tom", 20000, 2015, 7, 10); Manager carl = new Manager("Carl Cracker", 80000, 1987, 12, 15); carl.setSecretary(harry); Manager tony = new Manager("Tony Tester", 40000, 1990, 3, 15); tony.setSecretary(harry); Employee[] staff = new Employee[3]; staff[0] = harry; staff[1] = carl; staff[2] = tony; try { String fileName = "employee.dat"; ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(fileName)); out.writeObject(staff); out.close(); ObjectInputStream in = new ObjectInputStream(new FileInputStream(fileName)); Employee[] newStaff = (Employee[]) in.readObject(); for (Employee employee : newStaff) { System.out.println(employee); } in.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } class Employee implements Serializable { private static final long serialVersionUID = -167978670073609475L; private String name; private double salary; private Date hireDay; public Employee(String name, double salary, int year, int month, int dayOfMonth) { super(); this.name = name; this.salary = salary; GregorianCalendar calendar = new GregorianCalendar(year, month - 1, dayOfMonth); this.hireDay = calendar.getTime(); } public String getName() { return name; } public double getSalary() { return salary; } public Date getHireDay() { return hireDay; } public void raiseSalary(double byPercent) { double raise = salary * byPercent / 100; salary += raise; } @Override public String toString() { return getClass().getName() + " [name=" + name + ", salary=" + salary + ", hireDay=" + hireDay + "]"; } } class Manager extends Employee { private static final long serialVersionUID = -668252664793507835L; private Employee secretary; public Manager(String name, double salary, int year, int month, int dayOfMonth) { super(name, salary, year, month, dayOfMonth); } public void setSecretary(Employee secretary) { this.secretary = secretary; } @Override public String toString() { return super.toString() + " [secretary=" + secretary + "]"; } }
Output:
io.serial.Employee [name=Tom, salary=20000.0, hireDay=Fri Jul 10 00:00:00 CST 2015] io.serial.Manager [name=Carl Cracker, salary=80000.0, hireDay=Tue Dec 15 00:00:00 CST 1987] [secretary=io.serial.Employee [name=Tom, salary=20000.0, hireDay=Fri Jul 10 00:00:00 CST 2015]] io.serial.Manager [name=Tony Tester, salary=40000.0, hireDay=Thu Mar 15 00:00:00 CST 1990] [secretary=io.serial.Employee [name=Tom, salary=20000.0, hireDay=Fri Jul 10 00:00:00 CST 2015]]