谈谈J2SE中的序列化之接受默认序列化
http://tech.ddvip.com 2006年04月01日 社区交流
本文详细介绍谈谈J2SE中的序列化之接受默认序列化
数据一致性问题、约束问题
要知道序列化是另一种形式上的“public构造函数”,但他仅仅构造起对象,而不作任何的检查,这样人很不舒服,所以必要的检查是必须的,这利用了readObject()
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException{
in.defaultReadObject();//先反序列化对象
…进行检查与初始化
}
出于结构化的考虑,通常使用一个名为initialize的函数,负责检查与初始化,如果失败抛出异常。要保持检查与初始化是很容易被忘记的,这常常导致问题。另一个问题在于当父类没有加入readObject()的时候,子类很容易忘记要调用对应的initialize函数。这仿佛回到了当初为什么要引入构造函数的问题,原因就是防止子类忘记调用初始化函数引发各种问题。所以,如果要保持数据一致性,一定要加入readObject()。
安全问题
安全性的话题超出了本文的范畴,但是你应该要知道,有可能一个攻击者会对你的类准备一个恶意的数据流企图生成一个错误的类。当你需要确保你的对象数据安全的话,你一般可以利用上面的方法来检查,并初始化,但对于某些引用不好检查。解决方法就是对重要的部件进行保护性拷贝。这里推荐一个好方法,它不用保护性拷贝个别的域,而是直接保护性拷贝整个对象。这就是:
Object readResolve() throws ObjectStreamException;
这个方法的用途就是,他会紧接着readObject()调用。它将会利用返回的对象代替原来反序列化的对象。也就是原来readObject()反序列化的对象将会被立即的丢弃。
Object readResolve() throws ObjectStreamException{
return new Serial2(this.xxx1,this.xxx2);// xxx1、xxx2是刚刚反序列化得来的,这是一种保护性拷贝
}
这样的话虽然在时间上有所浪费,但是对于特别的重要而安全的类,可以使用这种方法。如果数据一致性问题、约束问题通过逐一检查来解决很麻烦,也可以利用这种方法,但要考虑好成本,和注意下面的局限性。 利用readResolve()有一个明显的缺点,就是当父类实现了readResolve(),子类将变得无丛下手。如果一个保护的或者是公有的父类的readResolve()存在,并且子类也没有改写它,将会使得子类反序列化的时候最终得到一个父类的对象,这既不是我们要得结果,也不容易发现这种错误。而让子类重写readResolve()无疑是一个负担。也就是说对于要继承的类而言,实现readResolve()来保护类不是一个好方法。我们只能利用第一种方法写一个保护性的readObject()。
所以我的建议是:一般情况下,只有对于final的类采用readResolve()来进行保护。
责编:豆豆技术应用