Java更多的库谜题83:诵读困难者的一神论
从前有一个人,他认为世界上只有一只不寻常的狗,所以他把下面的类写成了一个单件[gamma 95]:
public class dog extensions exception {
public static final dog instance = newdog();
私狗(){}
公串toString(){
返回“Woof”;
}
}
原来这个人做的是错的。可以在这个类之外创建第二个Dog实例而不反射吗?
这个类可能看起来像一个整体,但事实并非如此。问题是Dog扩展了Exception,Exception实现了java.io.Serializable这意味着Dog是可序列化的,反序列化会创建一个隐藏的构造函数。如下面的程序所示,如果序列化Dog。实例,然后反序列化字节序列,您将最终得到另一只狗。程序打印false,表示新的Dog实例和原来的不一样,还打印Woof,表示新的Dog实例也有相应的函数:
import Java . io . *;
public class CopyDog{ //不要与copycat
public static void main(String[]args){
Dog newDog =(Dog)deep copy(Dog。实例);
system . out . println(newDog = = Dog。实例);
system . out . println(newDog);
}
//这种方法非常慢,通常是个坏主意!
静态公共对象deepCopy(Object obj){
try {
ByteArrayOutputStream Bos =
new ByteArrayOutputStream();
新建ObjectOutputStream(bos)。writeObject(obj);
bytearray inputstream bin =
new bytearray inputstream(Bos . tobytearray());
返回新的ObjectInputStream(bin)。read object();
} catch(Exception e){
throw new IllegalArgumentException(e);
}
}
}
要纠正这个问题,您可以向Dog添加readResolve方法,该方法可以将隐藏的构造函数转换为隐藏的静态工厂,以返回原始的Dog [EJ项目2,57]。在Dog中添加这个方法后,CopyDog将打印true而不是false,表明那个“副本”实际上是原始实例:
私有对象Read Resolve(){
/不接受子替换!
返回实例;
}
这个难题的主要教训是,实现Serializable的singleton类必须有一个readResolve方法来返回它的实例。第二个教训是,由于实现Serializable的类的扩展或从Serializable扩展的接口的实现,我们可能会无意中实现Serializable。平台设计者的教训是,隐藏的构造函数,比如序列化中生成的构造函数,会让读者产生程序行为的错觉。
位律师回复
0条评论