什么导致 java.lang.IncompatibleClassChangeError?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1980452/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 02:02:23  来源:igfitidea点击:

What causes java.lang.IncompatibleClassChangeError?

javajarruntime-errorbinary-compatibility

提问by Zombies

I'm packaging a Java library as a JAR, and it's throwing many java.lang.IncompatibleClassChangeErrors when I try to invoke methods from it. These errors seem to appear at random. What kinds of problems could be causing this error?

我将 Java 库打包为 JAR,java.lang.IncompatibleClassChangeError当我尝试从中调用方法时,它会抛出很多s。这些错误似乎是随机出现的。什么类型的问题可能导致此错误?

采纳答案by notnoop

This means that you have made some incompatible binary changes to the library without recompiling the client code. Java Language Specification §13details all such changes, most prominently, changing non-staticnon-private fields/methods to be staticor vice versa.

这意味着您在没有重新编译客户端代码的情况下对库进行了一些不兼容的二进制更改。 Java 语言规范 §13详细说明了所有此类更改,最突出的是,将static非非私有字段/方法更改为static或反之亦然。

Recompile the client code against the new library, and you should be good to go.

针对新库重新编译客户端代码,您应该很高兴。

UPDATE: If you publish a public library, you should avoid making incompatible binary changes as much as possible to preserve what's known as "binary backward compatibility". Updating dependency jars alone ideally shouldn't break the application or the build. If you do have to break binary backward compatibility, it's recommendedto increase the major version number (e.g. from 1.x.y to 2.0.0) before releasing the change.

更新:如果您发布公共库,则应尽可能避免进行不兼容的二进制更改,以保留所谓的“二进制向后兼容性”。理想情况下,单独更新依赖 jar 不应破坏应用程序或构建。如果您确实必须破坏二进制向后兼容性,建议在发布更改之前增加主要版本号(例如从 1.xy 到 2.0.0)。

回答by linuxbuild

Your newly packaged library is not backward binary compatible(BC) with old version. For this reason some of the library clients that are not recompiled may throw the exception.

您新打包的库与旧版本不向后二进制兼容(BC)。因此,一些未重新编译的库客户端可能会引发异常。

This is a completelist of changes in Java library API that may cause clients built with an old version of the library to throw java.lang.IncompatibleClassChangeErrorif they run on a new one (i.e. breaking BC):

这是Java 库 API 中可能导致使用旧版本库构建的客户端抛出 java.lang 的更改的完整列表。IncompatibleClassChangeError如果它们在新的上运行(即破坏 BC):

  1. Non-final field become static,
  2. Non-constant field become non-static,
  3. Class become interface,
  4. Interface become class,
  5. if you add a new field to class/interface (or add new super-class/super-interface) then a static field from a super-interface of a client class C may hide an added field (with the same name) inherited from the super-class of C (very rare case).
  1. 非最终字段变为静态,
  2. 非常量场变为非静态,
  3. 类成为接口,
  4. 接口变成类,
  5. 如果向类/接口添加新字段(或添加新的超类/超接口),那么来自客户端类 C 的超接口的静态字段可能会隐藏从继承的添加字段(具有相同名称) C的超类(非常罕见的情况)。

Note: There are many other exceptionscaused by other incompatible changes: NoSuchFieldError, NoSuchMethodError, IllegalAccessError, InstantiationError, VerifyError, NoClassDefFoundErrorand AbstractMethodError.

注意:还有许多由其他不兼容更改引起的异常NoSuchFieldErrorNoSuchMethodErrorIllegalAccessErrorInstantiationErrorVerifyErrorNoClassDefFoundErrorAbstractMethodError

The better paper about BC is "Evolving Java-based APIs 2: Achieving API Binary Compatibility"written by Jim des Rivières.

关于 BC 的更好的论文是Jim des Rivières 撰写的“Evolving Java-based APIs 2: Achieving API Binary Compatibility”

There are also some automatic toolsto detect such changes:

还有一些自动工具可以检测此类变化:

Usage of japi-compliance-checker for your library:

为您的库使用 japi-compliance-checker:

japi-compliance-checker OLD.jar NEW.jar

Usage of clirr tool:

clirr 工具的使用:

java -jar clirr-core-0.6-uber.jar -o OLD.jar -n NEW.jar

Good luck!

祝你好运!

回答by Ogre Psalm33

I have also discovered that, when using JNI, invoking a Java method from C++, if you pass parameters to the invoked Java method in the wrong order, you will get this error when you attempt to use the parameters inside the called method (because they won't be the right type). I was initially taken aback that JNI does not do this checking for you as part of the class signature checking when you invoke the method, but I assume they don't do this kind of checking because you may be passing polymorphic parameters and they have to assume you know what you are doing.

我还发现,在使用 JNI,从 C++ 调用 Java 方法时,如果以错误的顺序将参数传递给被调用的 Java 方法,则在尝试使用被调用方法中的参数时会出现此错误(因为它们不会是正确的类型)。我最初感到惊讶的是,当您调用该方法时,JNI 并没有为您执行此检查作为类签名检查的一部分,但我认为他们不会进行此类检查,因为您可能正在传递多态参数,而他们必须假设您知道自己在做什么。

Example C++ JNI Code:

示例 C++ JNI 代码:

void invokeFooDoSomething() {
    jobject javaFred = FredFactory::getFred(); // Get a Fred jobject
    jobject javaFoo = FooFactory::getFoo(); // Get a Foo jobject
    jobject javaBar = FooFactory::getBar(); // Get a Bar jobject
    jmethodID methodID = getDoSomethingMethodId() // Get the JNI Method ID


    jniEnv->CallVoidMethod(javaFoo,
                           methodID,
                           javaFred, // Woops!  I switched the Fred and Bar parameters!
                           javaBar);

    // << Insert error handling code here to discover the JNI Exception >>
    //  ... This is where the IncompatibleClassChangeError will show up.
}

Example Java Code:

示例 Java 代码:

class Bar { ... }

class Fred {
    public int size() { ... }
} 

class Foo {
    public void doSomething(Fred aFred, Bar anotherObject) {
        if (name.size() > 0) { // Will throw a cryptic java.lang.IncompatibleClassChangeError
            // Do some stuff...
        }
    }
}

回答by Brian Topping

While these answers are all correct, resolving the problem is often more difficult. It's generally the result of two mildly different versions of the same dependency on the classpath, and is almost always caused by either a different superclass than was originally compiled against being on the classpath orsome import of the transitive closure being different, but generally at class instantiation and constructor invocation. (After successful class loading and ctor invocation, you'll get NoSuchMethodExceptionor whatnot.)

虽然这些答案都是正确的,但解决问题往往更加困难。它通常是对类路径的相同依赖的两个稍微不同版本的结果,并且几乎总是由与最初编译的超类不同传递闭包的某些导入不同引起的,但通常在类实例化和构造函数调用。(在成功加载类和调用 ctor 之后,你会得到NoSuchMethodException什么。)

If the behavior appears random, it's likely the result of a multithreaded program classloading different transitive dependencies based on what code got hit first.

如果行为看起来是随机的,则很可能是多线程程序根据首先命中的代码加载不同的传递依赖项的结果。

To resolve these, try launching the VM with -verboseas an argument, then look at the classes that were being loaded when the exception occurs. You should see some surprising information. For instance, having multiple copies of the same dependency and versions you never expected or would have accepted if you knew they were being included.

要解决这些问题,请尝试将 VM-verbose作为参数启动,然后查看发生异常时正在加载的类。您应该会看到一些令人惊讶的信息。例如,拥有相同依赖项和版本的多个副本,如果您知道它们被包含在内,则您从未预料到或会接受它们。

Resolving duplicate jars with Maven is best done with a combination of the maven-dependency-pluginand maven-enforcer-pluginunder Maven (or SBT's Dependency Graph Plugin, then adding those jars to a section of your top-level POM or as imported dependency elements in SBT (to remove those dependencies).

使用 Maven 解决重复的 jar 最好使用 Maven 下的maven-dependency-pluginmaven-enforcer-plugin(或 SBT 的Dependency Graph Plugin,然后将这些 jar 添加到顶级 POM 的一部分或作为导入的依赖项) SBT 中的元素(删除这些依赖项)。

Good luck!

祝你好运!

回答by stivlo

Another situation where this error can appear is with Emma Code Coverage.

可能出现此错误的另一种情况是 Emma Code Coverage。

This happens when assigning an Object to an interface. I guess this has something to do with the Object being instrumented and not binary compatible anymore.

将对象分配给接口时会发生这种情况。我想这与被检测的对象有关,并且不再是二进制兼容的。

http://sourceforge.net/tracker/?func=detail&aid=3178921&group_id=177969&atid=883351

http://sourceforge.net/tracker/?func=detail&aid=3178921&group_id=177969&atid=883351

Fortunately this problem doesn't happen with Cobertura, so I've added cobertura-maven-plugin in my reporting plugins of my pom.xml

幸运的是 Cobertura 不会出现这个问题,所以我在我的 pom.xml 的报告插件中添加了 cobertura-maven-plugin

回答by Nerrve

I've faced this issue while undeploying and redeploying a war with glassfish. My class structure was like this,

我在取消部署和重新部署与 glassfish 的战争时遇到了这个问题。我的班级结构是这样的

public interface A{
}

public class AImpl implements A{
}

and it was changed to

它被改为

public abstract class A{
}

public class AImpl extends A{
}

After stopping and restarting the domain, it worked out fine. I was using glassfish 3.1.43

停止并重新启动域后,它运行良好。我使用的是 glassfish 3.1.43

回答by denu

Please check if your code doesnt consist of two module projects that have the same classes names and packages definition. For example this could happen if someone uses copy-paste to create new implementation of interface based on previous implementation.

请检查您的代码是否不包含具有相同类名称和包定义的两个模块项目。例如,如果有人使用复制粘贴基于以前的实现创建新的接口实现,就会发生这种情况。

回答by Eng.Fouad

I had the same issue, and later I figured out that I am running the application on Java version 1.4 while the application is compiled on version 6.

我遇到了同样的问题,后来我发现我在 Java 1.4 版上运行该应用程序,而该应用程序是在 6 版上编译的。

Actually, the reason was of having a duplicate library, one is located within the classpath and the other one is included inside a jar file that is located within the classpath.

实际上,原因是有一个重复的库,一个位于类路径中,另一个包含在位于类路径中的 jar 文件中。

回答by wmorrison365

If this is a record of possible occurences of this error then:

如果这是此错误可能发生的记录,则:

I just got this error on WAS (8.5.0.1), during the CXF (2.6.0) loading of the spring (3.1.1_release) configuration where a BeanInstantiationException rolled up a CXF ExtensionException, rolling up a IncompatibleClassChangeError. The following snippet shows the gist of the stack trace:

我刚刚在 WAS (8.5.0.1) 上收到此错误,在 CXF (2.6.0) 加载 spring (3.1.1_release) 配置期间,BeanInstantiationException 引发了 CXF ExtensionException,引发了 IncompatibleClassChangeError。以下代码段显示了堆栈跟踪的要点:

Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.apache.cxf.bus.spring.SpringBus]: Constructor threw exception; nested exception is org.apache.cxf.bus.extension.ExtensionException
            at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:162)
            at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:76)
            at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:990)
            ... 116 more
Caused by: org.apache.cxf.bus.extension.ExtensionException
            at org.apache.cxf.bus.extension.Extension.tryClass(Extension.java:167)
            at org.apache.cxf.bus.extension.Extension.getClassObject(Extension.java:179)
            at org.apache.cxf.bus.extension.ExtensionManagerImpl.activateAllByType(ExtensionManagerImpl.java:138)
            at org.apache.cxf.bus.extension.ExtensionManagerBus.<init>(ExtensionManagerBus.java:131)
            [etc...]
            at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:147)
            ... 118 more

Caused by: java.lang.IncompatibleClassChangeError: 
org.apache.neethi.AssertionBuilderFactory
            at java.lang.ClassLoader.defineClassImpl(Native Method)
            at java.lang.ClassLoader.defineClass(ClassLoader.java:284)
            [etc...]
            at com.ibm.ws.classloader.CompoundClassLoader.loadClass(CompoundClassLoader.java:586)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:658)
            at org.apache.cxf.bus.extension.Extension.tryClass(Extension.java:163)
            ... 128 more

In this case, the solution was to change the classpath order of the module in my war file. That is, open up the war application in the WAS console under and select the client module(s). In the module configuration, set the class-loading to be "parent last".

在这种情况下,解决方案是更改我的 war 文件中模块的类路径顺序。即,在 WAS 控制台下打开 war 应用程序并选择客户端模块。在模块配置中,将类加载设置为“parent last”。

This is found in the WAS console:

这是在 WAS 控制台中找到的:

  • Applicatoins -> Application Types -> WebSphere Enterprise Applications
  • Click link representing your application (war)
  • Click "Manage Modules" under "Modules" section
  • Click link for the underlying module(s)
  • Change "Class loader order" to be "(parent last)".
  • 应用程序 -> 应用程序类型 -> WebSphere 企业应用程序
  • 单击代表您的应用程序的链接(战争)
  • 单击“模块”部分下的“管理模块”
  • 单击底层模块的链接
  • 将“类加载器顺序”更改为“(父级最后)”。

回答by Snekse

Documenting another scenario after burning way too much time.

在消耗太多时间后记录另一个场景。

Make sure you don't have a dependency jar that has a class with an EJB annotation on it.

确保您的依赖项 jar 中没有带有 EJB 注释的类。

We had a common jar file that had an @localannotation. That class was later moved out of that common project and into our main ejb jar project. Our ejb jar and our common jar are both bundled within an ear. The version of our common jar dependency was not updated. Thus 2 classes trying to be something with incompatible changes.

我们有一个带有@local注释的通用 jar 文件。该类后来从那个公共项目中移出,并进入了我们的主要 ejb jar 项目。我们的 ejb jar 和我们的普通 jar 都捆绑在一个耳朵里。我们常见的 jar 依赖的版本没有更新。因此,2 个类试图成为具有不兼容变化的东西。