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...
}
}
}
就我而言,我以这种方式遇到了这个错误 . pom.xml 我的项目定义了两个依赖项 A 和 B . 并且 A 和 B 都定义了对相同工件(称为 C )的依赖,但它的不同版本( C.1 和 C.2 ) . 当发生这种情况时,对于 C 中的每个类,maven只能从两个版本中选择一个版本的类(同时构建uber-jar) . 它将根据dependency mediation规则选择"nearest"版本,并输出警告"We have a duplicate class..."如果版本之间的方法/类签名发生变化,如果在运行时使用了错误的版本,则会导致 java.lang.IncompatibleClassChangeError 异常 .
高级:如果 A 必须使用 C 的v1且 B 必须使用 C 的v2,那么我们必须relocateC 在 A 和 B 的poms中以避免类冲突(我们有一个重复的类警告)在构建依赖于两者的最终项目时 A 和 B .
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
17 回答
加上我的2美分 . 如果你使用scala和sbt以及scala-logging作为依赖项;那么这可能发生,因为scala-logging的早期版本的名称为scala-logging-api.So;本质上,依赖项解析不会因为不同而发生启动scala应用程序时导致运行时错误的名称 .
我有同样的问题,后来我发现我在Java版本1.4上运行应用程序,而应用程序是在版本6上编译的 .
实际上,原因是有一个重复的库,一个位于类路径中,另一个包含在一个jar文件中位于类路径中 .
这意味着您已对库进行了一些不兼容的二进制更改,而无需重新编译客户端代码 . Java Language Specification §13详细说明所有此类更改,最突出的是,将非私有字段/方法更改为
static
,反之亦然 .针对新库重新编译客户端代码,您应该很高兴 .
更新:如果您发布了一个公共库,您应该尽可能避免使不兼容的二进制更改保留's known as 3023136 . Updating dependency jars alone ideally shouldn't破坏应用程序或构建 . 如果你必须打破二进制向后兼容性,那么在发布更改之前,需要recommended来增加主要版本号(例如从1.x.y到2.0.0) .
您新打包的库 is not backward binary compatible (BC)与旧版本 . 因此,一些未重新编译的库客户端可能会抛出异常 .
这是Java库API中的一个 complete 更改列表,可能导致使用旧版本库构建的客户端抛出java.lang . IncompatibleClassChangeError 如果他们运行一个新的(即破坏BC):
非最终字段变为静态,
非常量字段变为非静态字段,
类成为接口,
接口成为类,
如果向类/接口添加新字段(或添加新的超类/超级接口),则来自客户端类C的超级接口的静态字段可能隐藏从中继承的添加字段(具有相同名称)超级C(非常罕见的情况) .
Note :由于其他不兼容的更改导致许多 other exceptions :NoSuchFieldError,NoSuchMethodError,IllegalAccessError,InstantiationError,VerifyError,NoClassDefFoundError和AbstractMethodError .
有关卑诗省的更好的论文是由JimdesRivières撰写的"Evolving Java-based APIs 2: Achieving API Binary Compatibility" .
还有一些 automatic tools 来检测这样的变化:
japi-compliance-checker
clirr
japitools
sigtest
japi-checker
使用japi-compliance-checker为您的图书馆:
clirr工具的用法:
祝好运!
虽然这些答案都是正确的,但解决问题往往更加困难 . 它通常是类路径上相同依赖关系的两个稍微不同版本的结果,并且几乎总是由不同于最初编译的超类导致在类路径上或者传递闭包的某些导入是不同的,但通常在类中实例化和构造函数调用 . (成功加载类和ctor后,你会得到
NoSuchMethodException
或诸如此类的东西 . )如果行为看起来是随机的,那么很可能是多线程程序根据首先命中的代码加载不同的传递依赖性的结果 .
要解决这些问题,请尝试使用
-verbose
作为参数启动VM,然后查看发生异常时正在加载的类 . 你应该看到一些令人惊讶的信息 . 例如,拥有相同依赖项和版本的多个副本,如果您知道它们被包含在内,则您从未预料到或将会接受这些副本 .使用Maven解决重复的jar最好使用Maven下的maven-dependency-plugin和maven-enforcer-plugin(或SBT的Dependency Graph Plugin,然后将这些jar添加到顶层POM的一部分或作为SBT中的导入依赖元素(以删除这些依赖项)) .
祝好运!
我还发现,在使用JNI时,从C调用Java方法,如果以错误的顺序将参数传递给调用的Java方法,当您尝试使用被调用方法中的参数时,将会出现此错误(因为它们将不是正确的类型) . 当你调用方法时,我最初认为JNI不会检查你作为类签名检查的一部分,但我认为它们不会进行这种检查,因为你可能正在传递多态参数而且他们必须假设你知道你在做什么 .
示例C JNI代码:
示例Java代码:
可能出现此错误的另一种情况是Emma Code Coverage .
将对象分配给接口时会发生这种情况 . 我想这与被检测的对象有关,而不再与二进制兼容 .
http://sourceforge.net/tracker/?func=detail&aid=3178921&group_id=177969&atid=883351
幸运的是,这个问题不会发生在Cobertura上,所以我在我的pom.xml的报告插件中添加了cobertura-maven-plugin
我在使用glassfish取消部署和重新部署战争时遇到了这个问题 . 我的 class 结构是这样的,
它变成了
停止并重新启动域后,它运行正常 . 我正在使用glassfish 3.1.43
我有一个Web应用程序,可以在我的本地机器的tomcat(8.0.20)上完美地部署 . 但是,当我把它放入qa环境(tomcat - 8.0.20)时,它继续给我IncompatibleClassChangeError,它抱怨我正在扩展接口 . 此接口已更改为抽象类 . 我编写了父类和子类,但我仍然继续遇到同样的问题 . 最后,我想调试,所以,我将父版本的版本更改为x.0.1-SNAPSHOT,然后编译所有内容,现在它正在运行 . 如果有人在按照此处给出的答案后仍然遇到问题,请确保pom.xml中的版本也正确无误 . 更改版本以查看是否有效 . 如果是这样,那么修复版本问题 .
我相信,我的答案将是Intellij具体的 .
我已经重建了干净,甚至可以手动删除“out”和“target”dirs . Intellij有一个“无效的缓存和重启”,有时会清除奇怪的错误 . 这次它没用 . 在项目设置 - >模块菜单中,依赖版本看起来都是正确的 .
最后的答案是从我当地的maven repo手动删除我的问题依赖项 . 旧版本的bouncycastle是罪魁祸首(我知道我刚刚更改了版本,这将是问题)虽然旧版本没有出现在正在构建的地方,但它解决了我的问题 . 我正在使用intellij版本14,然后在此过程中升级到15 .
就我而言,我以这种方式遇到了这个错误 .
pom.xml
我的项目定义了两个依赖项A
和B
. 并且A
和B
都定义了对相同工件(称为C
)的依赖,但它的不同版本(C.1
和C.2
) . 当发生这种情况时,对于C
中的每个类,maven只能从两个版本中选择一个版本的类(同时构建uber-jar) . 它将根据dependency mediation规则选择"nearest"版本,并输出警告"We have a duplicate class..."如果版本之间的方法/类签名发生变化,如果在运行时使用了错误的版本,则会导致java.lang.IncompatibleClassChangeError
异常 .高级:如果
A
必须使用C
的v1且B
必须使用C
的v2,那么我们必须relocateC
在A
和B
的poms中以避免类冲突(我们有一个重复的类警告)在构建依赖于两者的最终项目时A
和B
.请检查您的代码是否包含两个具有相同类名和包定义的模块项目 . 例如,如果有人使用复制粘贴来创建基于先前实现的接口的新实现,则可能发生这种情况 .
如果这是可能出现此错误的记录,那么:
在Spring(3.1.1_release)配置的CXF(2.6.0)加载期间,我刚刚在WAS(8.5.0.1)上遇到此错误,其中BeanInstantiationException卷起了一个CXF ExtensionException,卷起了IncompatibleClassChangeError . 以下代码段显示了堆栈跟踪的要点:
在这种情况下,解决方案是更改war文件中模块的类路径顺序 . 也就是说,在WAS控制台下打开war应用程序并选择客户端模块 . 在模块配置中,将类加载设置为“父最后一个” .
这可以在WAS控制台中找到:
Applicatoins - >应用程序类型 - > WebSphere Enterprise Applications
点击代表您的应用程序的链接(战争)
单击"Modules"部分下的"Manage Modules"
单击基础模块的链接
将"Class loader order"更改为"(parent last)" .
在刻录太多时间之后记录另一个场景 .
确保你没有有一个依赖jar,它有一个带有EJB注释的类 .
我们有一个共同的jar文件,它有一个
@local
注释 . 该课程后来被移出该共同项目并进入我们的主要ejb jar项目 . 我们的ejb jar和我们常见的jar都捆绑在耳朵里 . 我们常见的jar依赖项的版本没有更新 . 因此,有两个类试图成为具有不兼容变化的东西 .以上所有 - 无论出于什么原因,我正在做一些大型重构并开始得到这个 . 我重命名了我的界面所在的包,并清除了它 . 希望有所帮助 .
出于某种原因,在使用JNI并在调用
Call*Method()
时传递jclass参数而不是jobject时,也会抛出相同的异常 .这类似于Ogre Psalm33的答案 .
我知道在被问到5年后回答这个问题有点晚了,但这是搜索
java.lang.IncompatibleClassChangeError
时的热门点击之一,所以我想记录这个特例 .此问题的另一个原因是,如果您为Android Studio启用了
Instant Run
.修复
如果您发现开始出现此错误,请关闭
Instant Run
.Android Studio主要设置
构建,执行,部署
即时运行
Untick "Enable instant run..."
为什么
Instant Run
在开发过程中修改了大量内容,以便更快地为正在运行的应用程序提供更新 . 因此即时运行 . 当它工作时,它真的很有用 . 但是,当这样的问题发生时,最好的办法就是关闭Instant Run
,直到Android Studio的下一个版本发布 .