Java对象序列化就那些实现了Serializable接口的对象转换成一个字节序列,并能够在以后将这个字节序列完全恢复为原来的对象。这一过程甚至可以通过网络进行;运行Windows操作系统的计算机上创建的一个对象将其序列化,通过网络将它发送到一台运行Unix系统的计算机,然后在那里准确地重新组装。Java序列化的一个引用就是RMI,当向远程对象发送消息,需要通过对象序列化来传输参数和返回值。如下例所示:
import java.io.*;
import java.util.*;
public class Logon implements Serializable{
private Date date=new Date();
private String username;
private transient String password; //加transient表示不进行序列化,所以输出为(n/a)
public Logon(String name,String pwd){
username=name;
password=pwd;
}
public String toString(){
String pwd=(password==null)?"(n/a)":password;
return "Long info: \n username: "+username+"\n date: "+date+"\n password: "+pwd;
}
public static void main(String[] args)throws Exception{
Logon a=new Logon("Hulk","myLittleBoy");
System.out.println("Logon a = "+a);
ObjectOutputStream o= new ObjectOutputStream(new FileOutputStream("Logon.out"));
o.writeObject(a);
o.close();
Thread.sleep(3000);
ObjectInputStream in= new ObjectInputStream(new FileInputStream("Logon.out"));
System.out.println("Recovering object at "+new Date());
a=(Logon)in.readObject();
System.out.println("logon a="+a);
}
}
编译通过,运行结果如下:
Logon a = Long info:
username: Hulk
date: Fri Sep 22 17:13:28 CST 2006
password: myLittleBoy
Recovering object at Fri Sep 22 17:13:31 CST 2006
logon a=Long info:
username: Hulk
date: Fri Sep 22 17:13:28 CST 2006
password: (n/a)
a.dateFri Sep 22 17:13:28 CST 2006
从输出结果可以看到,即对象被重新恢复。如果对象内包含了其他对象的引用,那么这些引用也会被保存并且被恢复出来,如上Date对象的引用date。
如果需要对序列化进行控制,可以实现Externalizable接口(代替实现Serializable接口)。这个Externalizable接口继承了Serializable接口并且增加了两个方法:writeExternal()和readExternal()。这两个方法会在序列化和反序列化过程中被自动调用,以便执行一些特殊操作。如下所示:
import java.io.*;
import java.util.*;
public class Blip implements Externalizable{
private int i;
private String s;
public Blip(){ //pulic标识符不能少
System.out.println("Blip Constructor.");
}
public Blip(String x,int a){
System.out.println("Blip(String x,int a)");
s=x;
i=a;
}
public String toString(){return s +" "+i;};
public void writeExternal(ObjectOutput out)
throws IOException{
System.out.println("Blip.writeExternal");
out.writeObject(s);
out.writeInt(i);
}
public void readExternal(ObjectInput in)
throws IOException,ClassNotFoundException{
System.out.println("Blip.readExternal");
s=(String)in.readObject();
i=in.readInt();
}
public static void main(String[] args)
throws IOException,ClassNotFoundException{
System.out.println("Constructing Object:");
Blip b=new Blip("A String",47);
System.out.println(b);
ObjectOutputStream o= new ObjectOutputStream(new FileOutputStream("Blip.out"));
System.out.println("Saving Object:");
o.writeObject(b);
o.close();
ObjectInputStream in= new ObjectInputStream(new FileInputStream("Blip.out"));
System.out.println("Recovering b:");
b=(Blip)in.readObject();
System.out.println(b);
}
}
运行结果如下:
Constructing Object:
Blip(String x,int a)
A String 47
Saving Object:
Blip.writeExternal
Recovering b:
Blip Constructor.
Blip.readExternal
A String 47
从运行结果可以看到,由于我们的恢复和存储功能由writeExternal()和readExternal()完成,所以修改writeExternal()和readExternal()方法就可以控制序列化了。从结果还可以看到,恢复b后,会调用Blip缺省构造器,这个时候缺省构造器须是public的,否则运行时会抛出异常(能通过编译):
Exception in thread "main" java.io.InvalidClassException: Blip; no valid constructor。
当然也不能利用类提供的缺省构造器,因为类提供的构造起没有任何标识符的,所以就不是public的。所以必须写一个public的缺省构造器。