一般默认情况对对象进行序列化时会选择使用Serializable接口即可以了,而且Serializable接口不仅能序列化自己显示写进去的对象,而且会自动的追踪对象内的所包含的对象的引用,并且是迭代追踪的,省却了许多麻烦。
不过如果在对象进行序列化时如果希望对对象的某些部分不被序列化(其实也可以用transient关键字解决)或者在对象被反序列化时希望某些子对象或属性可以重建的话,那么就可以用另一种序列化方式:实现Externalizable接口,即可方便的解决这些问题,下面对这两种方式分别举例说明:
SerializableTest.java:
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; class A implements Serializable{ private int id; private String name; public A(){ } public A(int id,String name){ this.id=id; this.name=name; } public int getId() { return id; } public String getName() { return name; } } public class SerializableTest { public static void main(String[] args) throws IOException,ClassNotFoundException{ A a=new A(3,"test"); ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("test.out")); oos.writeObject(a); oos.close(); ObjectInputStream ois=new ObjectInputStream(new FileInputStream("test.out")); Object o=ois.readObject(); System.out.println(o.getClass()); A a1=(A)o; System.out.println(" "+a1.getId()+":"+a1.getName()); } }
上面的示例程序即时对serializable序列化机制的简单演示,其实serializable序列化实现是先将需要序列化的对象给转化成相应的二进制,然后存储到相应的硬盘设备或文件中,再反序列化时再完全以文件上的二进制位为基础构造出被序列化的对象,而不会涉及的对象的构造函数,数据均是从InputStream中获取的。
Externalizable序列化机制和Serializable有所不同,首先实现Externalizable接口必须要实现writeExternal(ObjectOutput out) 和void readExternal(ObjectInput in)两个方法,这两个方法分别在对象被序列化和反序列化时自动调用,可以从下面的示例中看出,而且Externalizable的反序列化和serializable不一样,它会在反序列化时调用对象的默认构造函数来创建这个对象,然后利用void readExternal(ObjectInput in)中的对相应的属性的进行初始化。如果没有该默认构造函数,会报错。
ExternalizableTest.java:
import java.io.Externalizable; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.io.ObjectOutputStream; class B implements Externalizable{ private int id; private String name; //如果没有默认构造函数时,会在反序列化时报错 public B(){ System.out.println("default Constructor called!"); } public B(int id,String name){ System.out.println("Constructor called!"); this.id=id; this.name=name; } //会在进行改对象的反序列化时被自动调用,这样可以在该方法中根据自要求有选择性的对子对象或者某些属性进行构建或读取 //如果没有对该方法中的属性(如下方法中的id和name进行初始化的话,那么id就会为0,name为null) public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { // TODO Auto-generated method stub System.out.println("readExternal finished!"); id=in.readInt(); name=(String)in.readObject(); } public void writeExternal(ObjectOutput out) throws IOException { // TODO Auto-generated method stub System.out.println("writeExtern finished!"); out.writeInt(id); out.writeObject(name); } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } public class ExternalizableTest { public static void main(String[] args) throws IOException,ClassNotFoundException{ ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("testb.out")); B b=new B(1,"test"); oos.writeObject(b); oos.close(); ObjectInputStream ois=new ObjectInputStream(new FileInputStream("testb.out")); B b1=(B)ois.readObject(); System.out.println(" "+b1.getId()+":"+b1.getName()); } }
程序运行结果如下:
Constructor called!
writeExtern finished!
default Constructor called!
readExternal finished!
1:test
可以看出这两种序列化的一些区别。