我对我的Serializable类进行了兼容的修改,即添加一些原始字段(boolean,long)。我的类是服务器提供的数据的客户端表示。
当我从文件加载类时,我需要能够检测加载的类是否是由早期版本保存的,即它缺少一些java在加载时设置为默认值的字段。如果是这种情况,我的应用程序仍然有效,但当客户端具有网络连接时,我想从服务器获取新字段。
Java是否有任何内置方法来检测是否加载了类缺少字段?
(我意识到我可以使用"版本"实例成员手动解决这个问题;我希望尽管Java内置了一些东西)
答案 0 :(得分:1)
您可以覆盖Serializable接口的readObject()方法,以自定义处理类的反序列化。
请注意强烈建议,每当您实施Serializable时,您还包含serialVersionUID:
private static final long serialVersionUID = 1;
如果你没有定义它,可以从你班级成员的哈希创建一个...然后如果你以后添加的内容如你所描述的那样,UID将不匹配,Java将不允许你重新加载旧数据
来自Serializable文档的更多信息:http://docs.oracle.com/javase/6/docs/api/java/io/Serializable.html
有关处理不间断更改的更多信息:
如果实现之间存在重大变化,那么您应该真的只更改serialVersionUID,这使得使用新代码反序列化旧版本毫无意义。
如果通过添加一些字段来修改具有非中断更改的类,则不应更改serialVersionUID。如果原始类没有指定的serialVersionUID,则会自动为您生成一个,并且将在您的新实现中自动生成一个与旧的不匹配的。
当您尝试使用serialVersionUID不匹配反序列化旧版本时,系统将抛出java.io.InvalidClassException。带有此异常的错误消息将告诉您旧的serialVersionUID是什么以及新的serialVersionUID是什么。
然后,您可以修改您的类,使其具有与旧版本匹配的显式指定的serialVersionUID,允许您反序列化旧版本和新版本,而不会抛出异常。
运行时将在反序列化旧版本实例时将任何新的基元成员设置为其默认值。
如果要检测是否已对该类的旧版本或新版本进行反序列化,则可以检查在readObject()实现中不应为默认值的默认值。
以下是一个例子:
// add a new private field to the modified class for tracking version:
private int myNewVersionFlag = 123;
// then in your readObject implementation:
private void readObject(java.io.ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
if( this.myNewVersionFlag == 0 ) {
// if we get here the object being deserialized must
// be an old version, so now set sensible defaults
// for the new params or whatever...
}
}
还有其他更复杂的方法,包括实现自己的ObjectInputStream子类,但总的来说,我发现简单地拥有一个不应该为零的版本字段对于许多用例来说简单而且足够。
<强>摘要强>
如果您始终指定自己的serialVersionUID,则代码更有效(它不必自动生成散列代码),您可以更好地明确处理未来的类更改。
如果您的更改中断,则只应更改此值,并且意味着无法对您的类的旧版本进行反序列化,因为该类的新功能无法进行合理的默认设置。
如果您有自己的私有成员用于跟踪版本号,则可以在readObject()中使用此成员,以便在需要时反序列化旧实例时向新变量插入合理的默认值。
最后,Java序列化规范中涵盖了您想要了解的有关反序列化的所有信息(但不敢提出):
http://docs.oracle.com/javase/7/docs/platform/serialization/spec/serialTOC.html
答案 1 :(得分:0)
是的,您可以按照readObject
方法执行操作。它不会检测是否缺少类字段,但您可以检查相关字段是否为空,这样您就可以做任何您需要做的事情。
http://docs.oracle.com/javase/7/docs/api/java/io/ObjectInputStream.html