181

我包装一个Java库作为JAR,它的投掷很多java.lang.IncompatibleClassChangeError时候我尝试从它调用的方法。这些错误似乎是随机出现的。什么样的问题可能导致这个错误?什么导致java.lang.IncompatibleClassChangeError?

+0

在我正在测试Apache FOP 1.0和Barcode4J的Eclipse项目中,Barcode4J附带的其他库显然覆盖了与FOP一起提供的库(某些版本号更高)。对于你在构建路径/类路径中放置的内容非常小心。 – Wivani 2011-06-15 08:06:21

回答

144

这意味着你已经到图书馆借了一些不兼容的二进制变化而无需重新编译客户端代码。 Java Language Specification §13细节所有这样的改变,最突出,改变非static非私有字段/方法是static或反之亦然。

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

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

+2

由于某些原因,此处的开发人员在重新编译客户端代码时无法准确解决问题。出于某种原因,如果他们编辑发生的文件,并重新编译该错误不再发生在那里,但更多将随机弹出项目中引用该库的项目。我很好奇什么可能会造成这种情况。 – Zombies 2009-12-30 15:42:36

+1

有没有动态生成的代码? – 2009-12-30 15:50:51

+4

你有没有尝试做一个干净的构建(删除所有'* .class'文件)并重新编译?编辑文件有类似的效果。 – notnoop 2009-12-30 15:52:52

87

你的新包装库不向后兼容二进制(BC)与旧版本。由于这个原因,一些未重新编译的库客户端可能会抛出异常。

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

  1. 非决赛场上成为静态,
  2. 非恒定的场成为非静态,
  3. 类变得接口,
  4. 接口成为类,
  5. 如果添加新的字段以类/接口(或添加新的超一流/超接口)从客户端类C的一个超接口可以隐藏一个添加的字段然后静态字段(具有相同名称)继承自C的超类(非常罕见的情况)。

注意:有许多其他异常其他引起不兼容的改变:NoSuchFieldError的NoSuchMethodErrorIllegalAccessErrorInstantiationError的VerifyErrorNoClassDefFoundError的AbstractMethodError

约公元前更好的纸张是"Evolving Java-based APIs 2: Achieving API Binary Compatibility"由吉姆·德Rivières酒店写入。

也有一些自动工具来检测这样的变化:

JAPI遵守检查工具

使用你的库:

japi-compliance-checker OLD.jar NEW.jar 

clirr工具的使用方法:

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

祝你好运!

+2

不错的清单!谢谢! – asgs 2016-04-11 21:59:53

4

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

实例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. 
} 

例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... 
     } 
    } 
} 
+0

感谢您的提示。调用具有错误实例(this)的Java方法时,我遇到了同样的问题。 – sstn 2014-09-19 14:18:06

47

虽然这些问题的答案是正确的,解决问题往往比较困难。它通常是对类路径具有相同依赖关系的两个不同版本的结果,并且几乎总是由不同于最初编译的类超类,而不是在类路径上导致,某些导入的传递闭包是不同的,但通常在类实例化和构造函数调用。 (成功的类加载和调用构造函数后,你会得到NoSuchMethodException或诸如此类的东西。)

如果行为似乎是随机的,它可能是一个多线程程序的类加载基于什么样的代码得到了先打不同的传递依赖的结果。

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

解决与Maven重复罐子最好与Maven(或SBT的Dependency Graph Pluginmaven-dependency-pluginmaven-enforcer-plugin配合完成的,然后加入那些罐子你的顶层POM的一部分或作为SBT进口的依赖元素(去除这些依赖)。

祝你好运!

+4

冗长的参数帮助我确定哪些罐子出问题。谢谢 – 2013-07-19 23:11:11

1

,同时取消部署我面对这个问题并用玻璃鱼重新部署一场战争。我的阶级结构是这样的,

public interface A{ 
} 

public class AImpl implements A{ 
} 

,并改为

public abstract class A{ 
} 

public class AImpl extends A{ 
} 

停止和重新启动域后,制定了罚款。 我正在使用glassfish 3.1.43

0

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

3

我有同样的问题,后来我想通了,我正在运行的Java版本1.4的应用程序,而应用程序是在版本编译6

其实,原因是有重复的库,一个位于类路径中,另一个位于类路径内的jar文件中。

+0

你是如何得到这个底部的?我确定我有一个类似的问题,但没有一个线索如何跟踪哪些依赖库被重复。 – beterthanlife 2013-05-08 10:21:42

+0

@beterthanlife编写一个脚本,在所有jar文件中搜索寻找重复类的脚本(通过全限定名称搜索,即使用包名称):) – 2013-05-08 10:30:11

+0

好的,使用NetBeans和maven-shade我想我可以看到所有的正在被复制的类以及它们所在的jar文件。我没有直接在我的pom.xml中引用这些jar文件中的很多,所以我认为它们必须被我的依赖包含在内。有没有一种简单的方法来找出哪些依赖包括他们,而不通过每个依赖的pom文件? (请原谅我的noobishness,我是一个Java处女!) – beterthanlife 2013-05-08 10:41:56

0

如果这是此错误的可能出现次数的记录,则:

我刚刚得到的是这样的错误(8.5.0.1),弹簧的CXF(2.6.0)加载过程中(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 

在这种情况下,解决办法是改变我的战争文件模块的类路径顺序。也就是说,打开WAS控制台下的战争应用程序并选择客户端模块。在模块配置中,将类加载设置为“最后父代”。

这在WAS控制台中发现:

  • Applicatoins - >应用程序类型 - > WebSphere企业应用程序
  • 点击链接代表您在“模块应用程序(WAR)
  • 点击 “管理模块” “部分
  • 点击链接为底层模块
  • 更改”类加载器顺序“为”(父最后)“。
0

记录另一种情况后,燃烧方式太多的时间。

请确保您没有依赖关系jar包含EJB标注的类。

我们有一个通用的jar文件,它有一个@local注释。这个班后来被移出了这个共同的项目,并进入我们的主要ejb jar项目。我们的ejb罐子和我们的普通罐子都捆在耳朵里。我们常见的jar依赖项的版本没有更新。因此,2类试图成为不兼容的变化。

0

所有上述 - 无论出于何种原因,我正在做一些大的重构,并开始得到这个。我重命名了我的接口所在的包,并将其清除。希望有所帮助。

1

我有一个web应用程序,在我的本地机器的tomcat(8.0.20)上部署得非常好。然而,当我把它放到qa环境中(tomcat - 8.0.20)时,它一直给我IncompatibleClassChangeError,它抱怨我正在扩展接口。该界面已更改为抽象类。我编译了父母和孩子班,但我仍然一直在争论同一个问题。最后,我想调试,所以,我将父版本更改为x.0.1-SNAPSHOT,然后编译所有内容,现在它正在工作。如果在遵循这里给出的答案之后仍有人遇到问题,请确保您的pom.xml中的版本也是正确的。更改版本以查看是否有效。如果是这样,那么修复版本问题。

1

我相信,我的答案将是Intellij特有的。

我已经重建干净,甚至可以手动删除“out”和“target”dirs。 Intellij有一个“无效缓存并重启”,有时会清除奇怪的错误。这次它不起作用。依赖版本在项目设置 - >模块菜单中看起来都是正确的。

最终答案是从我的本地maven回购库中手动删除我的问题依赖项。一个老版本的bouncycastle是罪魁祸首(我知道我刚刚更改过版本,这将是问题),虽然旧版本没有显示正在构建的地方,但它解决了我的问题。我在使用intellij版本14,然后在此过程中升级到15。

1

就我而言,我遇到了这样的错误。我的项目的pom.xml定义了两个依赖项AB。并且AB都定义了对相同工件(称为C)的依赖关系,但它的不同版本(C.1C.2)。发生这种情况时,对于C中的每个类,maven只能从两个版本中选择一个版本(同时构建uber-jar)。它将根据其dependency mediation规则选择“最近”版本,并将输出警告"We have a duplicate class..."如果版本之间方法/类签名发生更改,如果在运行时使用不正确的版本,则可能会导致java.lang.IncompatibleClassChangeError异常。

高级:如果建设项目的最后,当A必须使用C v1和B必须使用C V2,那么我们就必须relocateCAB的劲歌,以避免阶级矛盾(我们有一个重复类警告)这取决于AB

0

出于某种原因,当调用Call*Method()时,在使用JNI并传递jclass参数而不是jobject时也会抛出同样的异常。

这与Ogre Psalm33的答案类似。

void example(JNIEnv *env, jobject inJavaList) { 
    jclass class_List = env->FindClass("java/util/List"); 

    jmethodID method_size = env->GetMethodID(class_List, "size", "()I"); 
    long size = env->CallIntMethod(class_List, method_size); // should be passing 'inJavaList' instead of 'class_List' 

    std::cout << "LIST SIZE " << size << std::endl; 
} 

我知道这是一个有点晚被要求5年后要回答这个问题,但是这是java.lang.IncompatibleClassChangeError搜索时排名靠前的一个,所以我想记录这种特殊情况。

0

添加我的2美分如果您使用scala和sbt和scala-logging作为依赖关系;那么可能会发生这种情况,因为scala-logging的较早版本的名称为scala- logging-api.So;实质上依赖关系分辨率不因启动scala应用程序时导致运行时错误的不同名称而发生。

0

此问题的另一个原因是,如果您已为Android Studio启用​​。

的修复

如果你发现你开始得到这个错误,关闭​​。

  1. 的Android Studio主设置
  2. 建立,执行,部署
  3. 即时运行
  4. 取消选中 “启用即时运行...”

为什么

​​修改大开发过程中的事物数量,以便更快地为正在运行的应用程序提供更新。因此即时运行。当它起作用时,它非常有用。但是,如果出现此类问题,最好的方法是关闭​​,直到下一版Android Studio发布。

相关问题