【问题标题】:java.io.InvalidClassException: local class incompatible:java.io.InvalidClassException:本地类不兼容:
【发布时间】:2020-01-06 02:05:02
【问题描述】:

我创建了客户端和服务器,然后在客户端添加了一个类用于序列化目的,然后只需转到我硬盘中客户端的文件夹并将其复制粘贴到服务器对应的位置,classname.class 和 @分别为987654322@。

它在我自己的笔记本电脑上运行良好,但是当我想在其他系统上继续工作时,当我打开项目文件夹并且客户端尝试连接到服务器后,出现以下错误:

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;
}

【问题讨论】:

    标签: java serialization client-server deserialization


    【解决方案1】:

    如果一个类没有在代码中显式定义private static final long serialVersionUID,它将自动生成,并且不保证不同的机器会生成相同的id;看起来这正是发生的事情。 此外,如果类有任何不同(使用不同版本的类),自动生成的serialVersionUIDs 也会不同。

    来自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;
        ...
    

    【讨论】:

    • 如何调整定义中的serialVersionUID?
    • 这很奇怪。我会仔细检查双方是否都在使用最新版本的课程。
    • 我什至创建了两个同名的新项目并从一开始就创建了类,但它仍然发生
    • 尝试清理项目输出(编译时生成的.class 文件)并重建(重新编译)项目。
    • @EJP 尝试将 SUID 匹配到不同版本的类是不可取的。在两端更新类是正确的做法。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      Java 中的序列化并不意味着长期持久性或传输格式——它太脆弱了。由于类字节码和 JVM 的细微差别,您的数据不再可读。为您的任务使用 XML 或 JSON 数据绑定(XStream 快速且易于使用,并且有很多替代方案)

      【讨论】:

      • 我可以在一定程度上同意持久性,但使用 Java 序列化作为传输格式并没有错。如果一个人不知道像serialVersionUID's这样的Java序列化概念,那就太脆弱了。
      • 不是,真的。也许我们只是戴着不同颜色的眼镜看到了“Java 序列化”的世界。此外,它几乎是使用 RMI 时的唯一选择。另外,我看到您发表了一些关于“项目经理给某些东西蒙皮”的评论,后来被删掉了……
      • “类字节码和 JVM 的细微差别,您的数据不再可读”。这是不真实的。请参阅对象序列化规范的对象版本控制部分。
      • @EJP 这是一个链接:Versioning of Serializable Objects
      【解决方案4】:

      异常消息清楚地表明类版本(也包括类元数据)随着时间的推移发生了变化。也就是说,序列化时的类结构在反序列化时是不一样的。这很可能是“正在发生”的事情。

      【讨论】:

        【解决方案5】:

        可能发生的一件事:

        • 1:您使用给定库 A(版本 X)创建序列化数据
        • 2:然后您尝试使用相同的库 A(但版本 Y)读取此数据

        因此,在编译版本 X 时,JVM 将生成第一个 Serial ID(对于版本 X),它会对另一个版本 Y(另一个 Serial ID)执行相同的操作。

        当您的程序尝试反序列化数据时,它不能,因为这两个类没有相同的 Serial ID,并且您的程序不能保证两个 Serialized 对象对应于相同的类格式。

        假设您同时更改了构造函数,这对您来说应该是有意义的。

        【讨论】:

          【解决方案6】:

          如果您使用 oc4j 部署 ear。

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

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

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

          【讨论】:

            【解决方案7】:

            如果您使用的是 Eclipse IDE,请检查您的调试/运行配置。 在 Classpath 选项卡中,选择运行器项目并单击 Edit 按钮。 仅包括导出的条目必须选中。

            【讨论】:

              【解决方案8】:

              如果您使用 IntelliJ IDEA,请转到 File->Invalidate Caches。这将清除任何可能导致此问题的缓存类。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2012-01-10
                • 2018-04-21
                • 1970-01-01
                • 2020-10-08
                • 2018-07-20
                • 1970-01-01
                相关资源
                最近更新 更多