Java序列化 - java.io.InvalidClassException本地类不兼容

Dal*_*ale 56 java serialization deserialization

我有一个实现Serializable的公共类,它由多个其他类扩展.之前只有那些子类被序列化 - 从来不是超类.

超类定义了一个serialVersionUID.

我不确定它是否重要,但它没有标记为私有,而是它只是默认保护 - 你可能会说它是包保护

static final long serialVersionUID = -7588980448693010399L;
Run Code Online (Sandbox Code Playgroud)

但是,超类或任何子类都实现了readObject或writeObject,并且没有子类具有明确定义的serialVersionUID.我认为超类中定义的一个就足够了.

尽管如此,只要读回以前的序列化对象,直到新的实例变量,List/ArrayList以及新方法被添加到超类中,并且一些私有实例变量被添加到其子类之一,就可以了. .

现在,当尝试读回先前序列化的对象时,会抛出异常.一个类似于此:

com.SomeCompany.SomeSubClass; local class incompatible: stream classdesc serialVersionUID = 1597316331807173261, local class serialVersionUID = -3344057582987646196
Run Code Online (Sandbox Code Playgroud)

我假设这是因为默认的serialVersionUID,因为我没有在任何子类中声明一个而使用,现在由于超类和一个子类的变化而改变了.

关于如何摆脱这种困境的建议将不胜感激.我假设我需要实现readObject和writeObject,但除了调用defaultReadObject()和defaultWriteObject()之外,我不确定我需要做什么.我也不知道是否需要将serialVerisonUID添加到所有子类中,或者是否需要每个子类实现readObject和writeObject,或者我是否可以实现它们一次,假设我需要在超类中完成.

jta*_*orn 45

@DanielChapman给出了serialVersionUID的一个很好的解释,但没有解决方案.解决方案是:serialver在所有类上运行程序.将这些serialVersionUID值放在当前版本的类中.只要当前类与旧版本串行兼容,您应该没问题.(注意,将来的代码:你应该始终有一个serialVersionUID所有 Serializable班)

如果新版本不是串行兼容的,那么你需要用自定义readObject实现做一些魔术(writeObject如果你试图编写与旧代码兼容的类数据,你只需要一个自定义).一般来说,添加或删除类字段不会使类序列不兼容.通常会改变现有字段的类型.

当然,即使新类串行兼容的,您仍可能需要自定义readObject实现.如果你想填写从旧版本的类中保存的数据中缺少的任何新字段,你可能想要这个(例如,你有一个新的List字段,你想在加载旧的类数据时初始化为空列表).

  • ...或者如果您没有旧类,请从异常消息中获取旧的serialVersionUID,并根据需要重复. (5认同)

Dan*_*man 30

这里简短的回答是,如果您没有指定序列ID,则通过哈希计算序列ID.(静态成员不是继承的 - 它们是静态的,只有(1)并且它属于类).

http://docs.oracle.com/javase/6/docs/platform/serialization/spec/class.html

getSerialVersionUID方法返回此类的serialVersionUID.请参见第4.6节"流唯一标识符".如果未由类指定,则返回的值是使用国家标准协会定义的安全散列算法(SHA)从类的名称,接口,方法和字段计算的散列.

如果更改类或其层次结构,则哈希值将不同.这是一件好事.您的对象因其成员不同而不同.因此,如果您从序列化形式中读回来,它实际上是一个不同的对象 - 因此是例外.

答案很长,序列化非常有用,但除非没有其他办法,否则可能不应该用于持久化.它是一条危险的道路,特别是因为你正在经历的事情.您应该考虑数据库,XML,文件格式以及纯Java项目的JPA或其他持久性结构.


tax*_*eta 17

对我来说,我忘了添加默认的序列号.

private static final long serialVersionUID = 1L;
Run Code Online (Sandbox Code Playgroud)


skr*_*ngr 7

这对我有用:

如果您将Serialized类对象写入文件,然后对文件进行一些更改并对其进行编译,然后尝试读取对象,则会发生这种情况.

因此,如果修改并重新编译类,则再次将必要的对象写入文件.

PS:这不是解决方案; 本来是一个解决方法.