本文共 4978 字,大约阅读时间需要 16 分钟。
-- Start
序列化是对象持久化的一种技术, 要想实现序列化, 我们只需要让类实现 Serializable 接口即可, 该接口中没有任何方法, 它只起到标识作用, 然后我们可以通过 ObjectOutputStream 将对象持久化到文件中, 或使用 ObjectInputStream 重新构造对象, 下面是一个简单的例子.
import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;import java.util.Date;public class Test { public static void main(String[] args) throws Exception { User u = new User("ShangBo", new Date()); // 序列化对象 ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("user.dat")); out.writeObject(u); out.close(); // 反序列化对象 ObjectInputStream in = new ObjectInputStream(new FileInputStream("user.dat")); User nu = (User) in.readObject(); in.close(); // 打印姓名 System.out.println(nu.getName()); }}class User implements Serializable { private String name; private Date birthday; public User() { } public User(String name, Date birthday) { this.name = name; this.birthday = birthday == null ? null : (Date) birthday.clone(); // 保护性 copy } public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getBirthday() { return birthday == null ? null : (Date) birthday.clone(); // 保护性 copy } public void setBirthday(Date birthday) { this.birthday = birthday == null ? null : (Date) birthday.clone(); // 保护性copy }}看上去序列化对象是非常简单的, 事实上它是一种非常复杂的技术, 有许多地方需要我们注意.
我们可以在类中添加如下方法来干预序列化过程.
private void writeObject(java.io.ObjectOutputStream out) throws IOExceptionprivate void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundExceptionprivate void readObjectNoData() throws ObjectStreamExceptionANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;下面的例子演示了在父类没有实现 Serializable 接口时, 子类如何手动序列化父类的域.
import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;import java.util.Date;public class Test { public static void main(String[] args) throws Exception { User u = new User("ShangBo", new Date()); u.setID(123); // 序列化对象 ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("user.dat")); out.writeObject(u); out.close(); // 反序列化对象 ObjectInputStream in = new ObjectInputStream(new FileInputStream("user.dat")); User nu = (User) in.readObject(); in.close(); // 打印身份证号 System.out.println(nu.getID()); }}class Person { private Integer ID; // 父类必须有无参构造方法 public Person() { } public Integer getID() { return ID; } public void setID(Integer iD) { ID = iD; }}class User extends Person implements Serializable { private String name; private Date birthday; public User() { } public User(String name, Date birthday) { this.name = name; this.birthday = birthday == null ? null : (Date) birthday.clone(); // 保护性 copy } public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getBirthday() { return birthday == null ? null : (Date) birthday.clone(); // 保护性 copy } public void setBirthday(Date birthday) { this.birthday = birthday == null ? null : (Date) birthday.clone(); // 保护性copy } // 如下两个方法来干预序列化过程 // 反序列化的顺序必须和序列化的顺序一致 // 所以如果定义了 writeObject 方法, 我们必须同时定义 readObject 方法 private void writeObject(java.io.ObjectOutputStream out) throws IOException { // 首先调用默认的方法序列化 User 类的所有域 out.defaultWriteObject(); // 其次手动序列化父类的域 out.writeObject(getID()); } private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { // 反序列化的顺序必须和序列化的顺序一致 in.defaultReadObject(); setID((Integer) in.readObject()); }}如果你觉得 Java 默认的序列化机制不能满足你的要求或你无法忍受它的速度, Java 为我们提供了自定义序列化机制的能力, 我们只需要实现 Externalizable 接口, 然后实现它的两个方法即可, 但是我们必须手动序列化所有的域, 包括父类的域. 同时我们还必须考虑对象引用的问题, 如果多个对象引用同一个对象, 反序列化之后它们还是引用同一个对象吗? 总之, 序列化机制是非常复杂的, 我们尽可能不要自己实现序列化机制, 而是利用 Java 为我们提供的序列化机制.
还有一个非常重要的问题需要考虑, 如果我们把一个对象序列化到一个文件中, 经过一段时间后该对象所属的类已经改变, 此时如果我们反序列化之前的对象会发生什么呢?毫无疑问, 此时应该抛出异常, Java 是如何检测类是否发生了变化呢? Java 会在序列化时自动为对象分配一个版本号, 该版本号是通过一定的算法计算出来的, 我们可以通过以下三种方式来获取版本号:
System.out.println(ObjectStreamClass.lookup(String.class).getSerialVersionUID());
自动添加的这个版本号是非常敏感的, 对类的任何微小改动都有可能使版本号发生变化, 为此 Java 规范强烈推荐我们手动维护这个版本号, 我们可以在类中添加如下域来维护自己的版本号.
private static final long serialVersionUID = -3393497401990853857L;
---更多参见:
-- 声 明:转载请注明出处 -- Last Updated on 2012-06-08 -- Written by ShangBo on 2012-06-08 -- End