我有一个实现Serializable的公共类,它由多个其他类扩展。之前只有那些子类被序列化了 - 绝不是超类。
超类定义了一个serialVersionUID。
我不确定它是否重要,但它没有标记为私有,而是它只是默认保护 - 你可能会说它是受包保护的
static final long serialVersionUID = -7588980448693010399L;
超类,也没有任何子类,但是实现了readObject或writeObject,并且没有子类具有明确定义的serialVersionUID。我认为超类中定义的一个就足够了。
尽管如此,只要读回以前的序列化对象,直到新的实例变量,List / ArrayList以及新方法被添加到超类中,并且一些私有实例变量被添加到一个其子类。
现在,当尝试读回以前的序列化对象时,会抛出异常。一个类似于此:
com.SomeCompany.SomeSubClass; local class incompatible: stream classdesc serialVersionUID = 1597316331807173261, local class serialVersionUID = -3344057582987646196
我假设这是因为默认的serialVersionUID,因为我没有在任何子类中声明一个而使用,因为超类和一个子类中的更改而现在已经改变了。
如何摆脱这种困境的建议将不胜感激。我假设我需要实现readObject和writeObject,但除了调用defaultReadObject()和defaultWriteObject()之外,我不确定我需要做什么。我也不知道是否需要将serialVerisonUID添加到所有子类中,或者是否需要每个子类实现readObject和writeObject,或者我是否可以实现它们一次,假设我需要在超类中完成。
答案 0 :(得分:43)
@DanielChapman给出了serialVersionUID的一个很好的解释,但没有解决方案。解决方案是:在所有旧类上运行serialver
程序。将这些serialVersionUID
值放在当前版本的类中。只要当前类与旧版本串行兼容,您应该没问题。 (请注意将来的代码:总是 所有 serialVersionUID
类 {/ p>}
如果新版本不串行兼容,那么您需要使用自定义Serializable
实施做一些魔术(如果您是自定义readObject
则需要自定义writeObject
尝试编写与旧代码兼容的 new 类数据。一般来说,添加或删除类字段不会使类序列不兼容。通常会更改现有字段的类型。
当然,即使新类与串行兼容,您仍可能需要自定义readObject
实现。如果你想填写从旧版本的类中保存的数据中缺少的任何新字段,你可能想要这个(例如,你有一个新的List字段,你想在加载旧的类数据时初始化为空列表)。 / p>
答案 1 :(得分:28)
这里的简短回答是,如果您没有指定序列ID,则通过哈希计算序列ID。 (静态成员不是继承的 - 它们是静态的,只有(1)并且它属于该类。)
http://docs.oracle.com/javase/6/docs/platform/serialization/spec/class.html
getSerialVersionUID方法返回此的serialVersionUID 类。请参见第4.6节“流唯一标识符”。如果不 由类指定,返回的值是从中计算的哈希值 使用Secure Hash的类的名称,接口,方法和字段 算法(SHA)由美国国家标准协会定义。
如果更改类或其层次结构,则哈希值将不同。这是一件好事。您的对象因其成员不同而不同。因此,如果你从序列化形式中读回它,它实际上是一个不同的对象 - 因此是例外。
长的答案是序列化非常有用,但可能不应该用于持久性,除非没有其他方法可以做到。它是一条危险的道路,特别是因为你正在经历的事情。您应该考虑数据库,XML,文件格式以及纯Java项目的JPA或其他持久性结构。
答案 2 :(得分:17)
对我来说,我忘了添加默认的序列号。
private static final long serialVersionUID = 1L;
答案 3 :(得分:6)
这对我有用:
如果您将Serialized类对象写入文件,然后对文件进行了一些更改并对其进行了编译,然后尝试读取对象,则会发生这种情况。
因此,如果修改并重新编译类,则将必要的对象再次写入文件。
PS:这不是解决方案;本来是一个解决方法。