Java 语言支持一种称为对象序列化(Object Serialization)的非常通用的机制,可以将任何对象写入到流中,并在之后将其读回,首先需要支持对象序列化的类,必须继承与 Serializable 接口,该接口没有任何方法,只是对类起到标记的作用,然后使用 ObjectOutputStream 流来序列化对象,使用 ObjectInputStream 流来反序列化,示例代码如下:
public class Employee implements Serializable {
????????private String name;
????????private String sex;
????????public Employee() {
????????}
????????public Employee(String name, String sex) {
????????????????this.name = name;
????????????????this.sex = sex;
????????}
????????// getter 和 setter 方法
}
? ?
public class Manager extends Employee {
????????private Employee secretary;
????????public Manager(){
????????}
????????public Manager(String name, String sex) {
????????????????super(name, sex);
????????}
????????// getter 和 setter 方法
}
?Employee harry = new Employee("Harry Hacker", "男");
?Manager boss = new Manager("Carl Cracker", "女");
boss.setSecretary(harry);
? ObjectOutputStream outputStream = null;
?????????try {
??????????????? outputStream = new ObjectOutputStream(new FileOutputStream("serializableApp.dat"));
????????????????outputStream.writeObject(boss);
???????? } catch (FileNotFoundException ex) {
????????????????ex.printStackTrace();
???????? ?} finally {
????????????????if (outputStream != null) {
???????????????????????outputStream.close();
????????????????}
??????? ?}
????????????????ObjectInputStream inputStream = null;
????????????????try {
????????????????????????inputStream = new ObjectInputStream(new FileInputStream("serializableApp.dat"));
????????????????????????Manager serializableBoss = (Manager) inputStream.readObject();
????????????????????????System.out.println("manager name is " + serializableBoss.getName() + " sex is "
????????????????????????????????????????+ serializableBoss.getSex() + " secretary is "
????????????????????????????????????????+ serializableBoss.getSecretary().getName());
????????????????} catch (ClassNotFoundException ex) {
????????????????????????ex.printStackTrace();
????????????????} catch (FileNotFoundException ex) {
????????????????????????ex.printStackTrace();
????????????????} finally {
????????????????????????if (inputStream != null) {
????????????????????????????????inputStream.close();
????????????????????????}
????????????????}
每个对象都用一个序列号保存的,对象序列化机制如下:
某些数据域时不可以序列化的,例如,只对本地方法有意义的存储文件句柄或窗口句柄的整数值等,Java 拥有一种简单的机制来防止这种域被序列化,那就是将他们标记成 transient,如果被标记为不可序列化的类,也需要将其标记为 transient,瞬时域在对象序列化时总是被跳过,示例如下:
????private transient Point2D.Double point;
? ?
序列化机制单个的类提供了一种方式,去向默认的读写行为添加验证或任何其他想要的行为,可序列化类可以定义具体有如下签名的方法:
????????private void readObject(ObjectInputStream in)
????????????????????????throws IOException,ClassNotFoundException;
???????? ?
????????private void writeObject(ObjectOutputStream out)
????????????????????????throws IOException;
readObject 和 writeObject 方法只需要保存和加载本类的数据域,而不需要关注基类(超类)数据和任何其他类的信息,实现给方法的具体示例如下:
???? private void readObject(ObjectInputStream in)
????????????????????????throws IOException, ClassNotFoundException {
????????????????// 读取序列化字段数据
????????????????in.defaultReadObject();
????????????????// 其他数据校验或者序列化
????????}
????????private void writeObject(ObjectOutputStream out)
????????????????????????throws IOException{
????????????????// 写入序列化字段数据
????????????????out.defaultWriteObject();
????????????????// 其他数据校验或者序列化
???????}
除了可以使用readObject 和 writeObject方法来保存和恢复对象数据外,类还可以定义他自己的机制,类需要实现 Externalizable 接口,该接口定义了两个方法:
????public void readExternal(ObjectInput in)
???????? throws IOException, ClassNotFoundException;
? ?
????public void writeExternal(ObjectOutput out)
????????????throws IOException ;
这些方法对包括超类数据在内的整个对象的存储和恢复负全责,而序列化机制在流中仅仅只是记录该对象所属的类,示例代码如下:
????????public void writeExternal(ObjectOutput out) throws IOException {
????????????????out.writeUTF(this.name);
????????????????out.writeUTF(this.sex);
????????}
????????public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
????????????????this.name = in.readUTF();
????????????????this.sex = in.readUTF();
????????}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
????????super.readExternal(in);
????????this.secretary = new Employee();
??????? this.secretary.readExternal(in);
?}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
????????super.writeExternal(out);
???????this.secretary.writeExternal(out);
?}
? ?
在序列化和反序列化时,如果目标对象时唯一的,使用默认的序列化机制时不适用的,因为默认的序列化机制,即使构造器时私有的,序列化机制也可以创建新的对象,因此在进行==(比较)时将失败,为了解决整个问题需要定义一个名称为 readResolve的特殊方法,在对象被序列化之后就会调用他,返回一个对象,而该对象之后会称为 readObject 的返回值,示例代码如下:
public class Orientation implements Serializable {
????????public static final Orientation HORIZONTAL = new Orientation(1);
????????public static final Orientation VERTICAL = new Orientation(2);
? ?
????????private int value;
? ?
????????private Orientation(int value) {
????????????????this.value = value;
????????}
? ?
????????protected Object readResolve() throws ObjectStreamException {
????????????????if (value == 1) {
????????????????????????return HORIZONTAL;
????????????????}
????????????????if (value == 2) {
????????????????????????return VERTICAL;
????????????????}
???????????????? ?
????????????????return null;
????????}
}
无论类的定义产生了什么样的变化,他的SHA指纹也会跟着变化,而我们知道对象流拒绝读入具有不同指纹的对象,但是,类可以表明他对其早期版本保持兼容,在类的所有较新的版本都必须把 serialVersionUID 常量定义与最初版本的指纹相同,如果一个类具有名为 serialVersionUID 的静态数据成员,就不需要在人工的计算其指纹,而只需直接使用整个值,示例如下:
public class Employee implements Serializable, Externalizable {
????????public static final long serialVersionUID = -2349238498234324L;
}
如果类只有方法产生了变化,那么在读入新对象数据时是不会有任何问题的,如果是数据域产生了变化,那么就可能会有问题,常见情况如下:
? ?
原文:http://www.cnblogs.com/li3807/p/6810356.html