class Student implements Serializable{
private String name;
public Student(String name){
this.name = name;
}
public String getName(){
return name;
}
}
class StudentSerializer{
public static void main(String[] args) throws Exception{
// create a Student object
Student st = new Student("jason");
// serialize the st to jason.se file
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("jason.se"));
oos.writeObject(st);
oos.close();
// deserialize the object from jason.se
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("jason.se"));
Student jason = (Student) ois.readObject();
ois.close();
// verify the name field of jason object
assert "jason".equals(jason.getName());
}
}
从上例可以看出, JDK提供的序列化机制使用起来相当简单, 要注意的是:
JVM如何判断序列化与反序列化的类文件是否相同呢? 并不是说两个类文件要完全一样, 而是通过类的一个私有属性serialVersionUID来判断的, 如果我们没有显示的指定这个属性, 那么JVM会自动使用该类的hashcode值来设置这个属性, 这个时候如果我们对类进行改变(比如说加一个属性或者删掉一个属性)就会导致serialVersionUID不同, 所以对于准备序列化的类, 一般情况下我们都会显示的设置这个属性, 这样及时以后我们对该类进行了某些改动, 只要这个值保持一样, JVM就还是会认为这个类文件是没有变的, 需要注意的是这种改变不包括继承结构的变化. 该属性必须以下面的修饰方法来设置:
//当然这个值可以自己指定, 也可以通过JDK提供的serializer来查看其默认的hashcode值.
private static final long serialVersionUID = -4333316296251054416L;
由于JDK提供的这种默认的序列化机制是简单的将对象变成字节流, 有时候并不满足我们的要求, 比如考虑到加密, 或者在反序列化完了后需要调用某个方法来初始化transient的属性等等, JDK提供了一种扩展的方法来增加对序列化和反序列化的控制. 那就是可以让序列化的对象实现下面两个固定的方法(注意修饰符和结构是固定的, throws的exception可以变通, 比如直接写成throws Exception也是可以的):
private void writeObject(ObjectOutputStream oos) throws IOException {}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException{}
JVM在序列化和反序列化的过程中如果发现我们的类实现了这两个方法, 就会在writeObject(obj)和readObject()的时候将控制流转交给这两个方法, 这样我们就可以来执行一些额外的操作, 同时可以在这两个方法中调用ObjectOutputStream的defaultWriteObject()和ObjectInputStream的defaultReadObject()来让JVM帮我们来执行底部的具体序列化和反序列化操作, 如下例所示:
class Student implements Serializable{
private String name;
public Student(String name){
this.name = name;
}
public String getName(){
return name;
}
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
// 可以执行其他的操作, 比如对反列化的文件进行加密等等
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
//可以调用其他方法来进行额外的初始化操作
}
}
注意此时仅仅是对需要序列化的类增加了这两个私有方法, 而对如何将其序列化和反序列化上并没有任何改变, 也就是说我们的StudentSerializer类并不需要做任何改变, JVM的序列化机制会自行来控制, 当然如果我们在两个私有的writeObject()和readObject()中并没有调回ObjectOutputStream的defaultWriteObject()和ObjectInputStream的defaultReadObject()方法的话, 那就并没有序列化了(除非你自己实现这个序列化和反序列化的过程).