Java.io.InvalidClassException: 本地类不兼容:

65

我创建了客户端和服务器,然后在客户端添加了一个类用于序列化目的,接着只需到硬盘上的客户端文件夹中复制粘贴到服务器对应位置,分别是 classname.classclassname.java

在我的笔记本电脑上运行良好,但当我想要继续在其他系统上工作时,当客户端尝试连接到服务器时,出现以下错误:

Exception in thread "main" java.io.InvalidClassException: projectname.clasname; local class incompatible: stream classdesc serialVersionUID = -6009442170907349114, local class serialVersionUID = 6529685098267757690
    at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:562)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1582)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1495)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1731)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)

发生了什么?是因为我使用了旧版本的IDE运行程序吗?

编辑

import java.io.Serializable;
import java.net.URL;

public class KeyAdr implements Serializable {
  private static final long serialVersionUID = 6529685098267757690L;

  public URL adr;
  public String key;
}
10个回答

96
如果一个类在代码中没有明确定义private static final long serialVersionUID,那么它将自动生成,而且不能保证不同的机器会生成相同的ID;看起来这正是发生的情况。此外,如果类有任何不同之处(使用不同版本的类),自动生成的serialVersionUID也将不同。
Serializable接口的docs中:
如果一个可序列化的类没有显式地声明serialVersionUID,那么序列化运行时将根据类的各个方面计算一个默认的serialVersionUID值,如Java(TM)对象序列化规范中所述。然而,强烈建议所有可序列化的类都显式声明serialVersionUID值,因为默认的serialVersionUID计算对编译器实现可能会有所不同的类细节非常敏感,因此在反序列化过程中可能导致意外的InvalidClassExceptions。因此,为了保证不同java编译器实现下一致的serialVersionUID值,可序列化的类必须声明一个显式的serialVersionUID值。同时也强烈建议显式的serialVersionUID声明尽可能使用private修饰符,因为这样的声明仅适用于直接声明类 - serialVersionUID字段作为继承成员是无用的。数组类不能声明显式的serialVersionUID,因此它们总是具有默认计算值,但匹配serialVersionUID值的要求对于数组类被豁免。

在类定义中,您应该定义一个 serialVersionUID,例如:

class MyClass implements Serializable {
    private static final long serialVersionUID = 6529685098267757690L;
    ...

1
很奇怪。我会再次确认双方是否都在使用最新版本的类。 - trutheality
5
尝试清理项目输出(即编译时生成的.class文件),然后重新构建(重新编译)项目。 - yair
1
@EJP 尝试将 SUID 与不同版本的类匹配并不可取。在两端更新类是正确的做法。 - trutheality
感谢分享这些信息。请记住,这里的所有关键字都是必需的 -- private static final long serialVersionUID = -476368215744086631L; - littletiger
你的回答和jtahlborn的回答结合起来包含了所有相关细节。也许这些问题可以合并一下? - jpaugh
显示剩余9条评论

13

可能发生的一种情况:

  • 1:你用给定的A库(版本X)创建了序列化数据
  • 2:然后你试图用同样的A库(但是版本Y)读取此数据

因此,在版本X的编译时,JVM将生成第一个Serial ID(针对版本X),并且对另一个版本Y(另一个Serial ID)也是如此。

当你的程序尝试反序列化数据时,它无法完成,因为两个类的Serial ID不相同,你的程序不能确保两个序列化对象对应于相同的类格式。

假设在此期间您更改了构造函数,这对您来说应该有意义。


可能是你在类中添加了一个字段,但在序列化的对象中却没有这样的字段。 - undefined

1
如果您正在使用IntelliJ IDEA,请转到“文件”->“无效缓存”。这将清除可能导致此问题的任何缓存类。

1

如果您在测试过程中存储了一个对象,请尝试删除/重命名输出文件并重新创建一个新的文件。我曾经因为在两个不同的jFrame类中将数据序列化到同一个文件中而遇到了这个错误。


1
如果您正在使用Eclipse IDE,请检查您的调试/运行配置。在Classpath选项卡中,选择运行项目并单击“编辑”按钮。必须勾选“仅包括已导出的条目”。

0

如果您正在使用oc4j部署ear文件。

请确保在项目中设置正确的deploy.home路径=

您可以在common.properties文件中找到deploy.home

oc4j需要重新加载ear中新创建的类,以便服务器类和客户端类具有相同的serialVersionUID


0
异常信息明确表明类版本已经随时间改变,这将包括类元数据。换句话说,在序列化期间的类结构与反序列化期间不同。这很可能是发生的情况。

0

自动生成的 serialVersionUID 在不同的操作系统或 JDK 版本中是相同的。 但如果您添加了一个函数或一个字段,自动生成的 serialVersionUID 将会改变。


0
我相信这是因为你在客户端和服务器上使用了不同版本的同一个类。 可能是不同的数据字段或方法。

0
Java中的序列化并不适用于长期持久性或传输格式 - 它太脆弱了。只要类字节码和JVM有最小的差异,您的数据就无法读取。使用XML或JSON数据绑定来完成您的任务(XStream快速易用,并且有大量替代方案)。

2
我可以在某种程度上同意持久性的观点,但使用Java序列化作为传输格式并没有什么问题。如果一个人不了解Java序列化的概念,比如serialVersionUID,那么它就太脆弱了。 - Sanjay T. Sharma
其实并不是这样。也许我们只是用不同的眼光看待“Java序列化”的世界。此外,在使用RMI时,这几乎是唯一的选择。另外,我看到你曾经评论过“项目经理剥皮某些东西”,但后来被编辑掉了... - Sanjay T. Sharma
“类字节码和JVM稍有不同,你的数据就无法读取”这种说法是不正确的。请参阅对象序列化规范中的对象版本控制部分。 - user207421
@EJP 这是一个链接:可序列化对象的版本控制 - jpaugh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接