风舞残阳 2008-4-1 11:22
J2SE中的序列化之继承
当一个父[wiki]类[/wiki]实现Serializable接口后,他的子类都将自动的实现序列化。
以下验证了这一点:
packageSerial;
importjava.io.Serializable;
publicclassSuperCimplementsSerializable{//父类实现了序列化
intsupervalue;
publicSuperC(intsupervalue){
this.supervalue=supervalue;
}
publicStringtoString(){
return"supervalue:"+supervalue;
}
}
publicclassSubCextendsSuperC{//子类
intsubvalue;
publicSubC(intsupervalue,intsubvalue){
super(supervalue);
this.subvalue=subvalue;
}
publicStringtoString(){
returnsuper.toString()+"sub:"+subvalue;
}
}
publicclass[wiki]Test[/wiki]1{
publicstaticvoidmain(String[]args){
SubCsubc=newSubC(100,200);
FileInputStreamin=null;
FileOutputStreamout=null;
ObjectInputStreamoin=null;
ObjectOutputStreamoout=null;
try{
out=newFileOutputStream("Test1.txt");//子类序列化
oout=newObjectOutputStream(out);
oout.writeObject(subc);
oout.close();
oout=null;
in=newFileInputStream("Test1.txt");
oin=newObjectInputStream(in);
SubCsubc2=(SubC)oin.readObject();//子类反序列化
System.out.println(subc2);
}catch(Exceptionex){
ex.printStackTrace();
}finally{
…此处省略
}
}
}
运行结果如下:
supervalue:100sub:200
可见子类成功的序列化/反序列化了。
怎管让子类实现序列化看起来是一件很简单的事情,但有的时候,往往我们不能够让父类实现Serializable接口,原因是有时候父类是抽象的(这并没有关系),并且父类不能够强制每个子类都拥有序列化的能力。换句话说父类设计的目的仅仅是为了被继承。
要为一个没有实现Serializable接口的父类,编写一个能够序列化的子类是一件很麻烦的事情。javadocs中提到:
“Toallowsub[wiki]type[/wiki]sofnon-serializableclassestobeserialized,thesubtypemayassumeresponsibilityforsavingandrestoringthestateofthesupertype'spublic,protected,and(ifaccessible)packagefields.Thesubtypemayassumethisresponsibilityonlyiftheclassitextendshasanaccessibleno-argconstructortoinitializetheclass'sstate.ItisanerrortodeclareaclassSerializableifthisisnotthecase.Theerrorwillbedetectedatruntime.”
也就是说,要为一个没有实现Serializable接口的父类,编写一个能够序列化的子类要做两件事情:
其一、父类要有一个无参的constructor;
其二、子类要负责序列化(反序列化)父类的域。
我们将SuperC的Serializable接口去掉,而给SubC加上Serializable接口。运行后产生错误:
java.lang.Error:Unresolvedcompilationproblem:
Serializablecannotberesolvedorisnotavalidsuperinterface
atSerial.SubC.(SubC.java:15)
atSerial.Test1.main(Test1.java:19)
Exceptioninthread"main"
果真如docs中所说的一样,父类缺少无参构造函数是不行的。
接下来,按照docs中的建议我们改写这个例子:
publicabstractclassSuperC{
intsupervalue;
publicSuperC(intsupervalue){
this.supervalue=supervalue;
}
publicSuperC(){}//增加一个无参的constructor
publicStringtoString(){
return"supervalue:"+supervalue;
}
}
publicclassSubCextendsSuperCimplementsSerializable{
intsubvalue;
publicSubC(intsupervalue,intsubvalue){
super(supervalue);
this.subvalue=subvalue;
}
publicStringtoString(){
returnsuper.toString()+"sub:"+subvalue;
}
privatevoidwriteObject(java.io.ObjectOutputStreamout)
throwsIOException{
out.defaultWriteObject();//先序列化[wiki]对象[/wiki]
out.writeInt(supervalue);//再序列化父类的域
}
privatevoidreadObject(java.io.ObjectInputStreamin)
throwsIOException,ClassNotFoundException{
in.defaultReadObject();//先反序列化对象
supervalue=in.readInt();//再反序列化父类的域
}
}
运行结果证明了这种方法是正确的。在此处我们用到了writeObject/readObject方法,这对方法如果存在的话,序列化时就会被调用,以代替默认的行为(以后还要探讨,先了解这么多)。我们在序列化时,首先调用了ObjectOutputStream的defaultWriteObject,它使用默认的序列化行为,然后序列化父类的域;反序列化的时候也一样。
归纳一下:
目的行为
为一个实现Serializable接口的父类,编写一个能够序列化的子类子类将自动的实现序列化
为一个没有实现Serializable接口的父类,编写一个能够序列化的子类1,父类要有一个无参的constructor;2,子类要先序列化自身,然后子类要负责序列化父类的域