Answers:
这意味着您对库进行了一些不兼容的二进制更改,而无需重新编译客户端代码。 Java语言规范§13详细介绍了所有这些更改,最显着的是将static
非非私有字段/方法更改为static
,反之亦然。
根据新库重新编译客户端代码,您应该一切顺利。
更新:如果发布公共库,则应尽可能避免进行不兼容的二进制更改,以保留所谓的“二进制向后兼容”。理想情况下,仅更新依赖项jar不会破坏应用程序或构建。如果必须中断二进制的向后兼容性,建议在发布更改之前增加主版本号(例如从1.xy到2.0.0)。
*.class
文件)并重新编译?编辑文件具有类似的效果。
您新包装的库不向后二进制兼容与旧版本(BC)。因此,某些未重新编译的库客户端可能会引发异常。
这是Java库API中更改的完整列表,这些更改可能导致使用旧版本库构建的客户端抛出java.lang。如果它们在新的(例如,打破BC)上运行,则为IncompatibleClassChangeError:
注意:还有许多其他不兼容的更改引起的异常:NoSuchFieldError,NoSuchMethodError,IllegalAccessError,InstantiationError,VerifyError,NoClassDefFoundError和AbstractMethodError。
关于BC的更好的论文是Jim desRivières撰写的“正在发展的基于Java的API 2:实现API二进制兼容性”。
还有一些自动工具可以检测到此类更改:
japi-compliance-checker在您的库中的用法:
japi-compliance-checker OLD.jar NEW.jar
clirr工具的用法:
java -jar clirr-core-0.6-uber.jar -o OLD.jar -n NEW.jar
祝好运!
尽管这些答案都是正确的,但解决问题通常更加困难。通常,这是对类路径的相同依赖关系的两个版本的略有不同的结果,并且几乎总是由与最初针对类路径的不同的超类或与传递闭包的某些导入不同而导致的,但通常是在类上实例化和构造函数调用。(在成功加载类和调用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代码:
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...
}
}
}
我遇到了同样的问题,后来我发现我在Java版本1.4上运行该应用程序,而该应用程序是在版本6上编译的。
实际上,原因是有一个重复的库,一个库位于类路径内,而另一个库包含在位于类路径内的jar文件内。
可能出现此错误的另一种情况是Emma代码覆盖率。
将对象分配给接口时,会发生这种情况。我猜想这与被检测的对象有关,不再与二进制兼容。
http://sourceforge.net/tracker/?func=detail&aid=3178921&group_id=177969&atid=883351
幸运的是,Cobertura不会发生此问题,因此我在pom.xml的报告插件中添加了cobertura-maven-plugin
我相信我的答案将取决于Intellij。
我已经重建干净,甚至可以手动删除“ out”和“ target”目录。Intellij具有“使缓存无效并重新启动”的功能,该功能有时会清除奇数错误。这次没有用。在项目设置->模块菜单中,所有依赖版本都看起来正确。
最后的答案是从本地Maven存储库中手动删除我的问题依赖项。罪魁祸首是旧版本的罪魁祸首(我知道我刚刚更改了版本,这就是问题所在),尽管旧版本在构建的内容中没有显示任何位置,但它解决了我的问题。我使用的是intellij版本14,然后在此过程中升级到15。
就我而言,我以这种方式遇到了这个错误。pom.xml
我的项目中定义了两个依赖A
和B
。而且两者A
并B
在同一工件(称之为定义的依赖C
),但不同版本的它(C.1
和C.2
)。发生这种情况时,对于C
maven中的每个类,只能从两个版本中选择一个版本(在构建uber-jar时)。它将根据其依赖关系调解规则选择“最近”版本,并输出警告“我们有重复的类...”。如果版本之间的方法/类签名发生更改,则java.lang.IncompatibleClassChangeError
如果版本不正确,则会导致异常在运行时使用。
高级:如果A
必须使用V1的C
和B
必须使用V2 C
,那么我们就必须重新定位 C
在A
和B
构建既取决于最终的项目时的,以避免阶级冲突劲歌(我们有一个重复类警告)A
和B
。
在我的情况下,当我com.nimbusds
在上部署的应用程序中添加库时出现了错误Websphere 8.5
。
发生以下异常:
引起原因:java.lang.IncompatibleClassChangeError:org.objectweb.asm.AnnotationVisitor
解决方案是从库中排除asm jar:
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>5.1</version>
<exclusions>
<exclusion>
<artifactId>asm</artifactId>
<groupId>org.ow2.asm</groupId>
</exclusion>
</exclusions>
</dependency>
如果这是此错误可能发生的记录,则:
在弹簧(3.1.1_release)配置的CXF(2.6.0)加载期间,我刚刚在WAS(8.5.0.1)上收到此错误,其中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
在这种情况下,解决方案是更改我的war文件中模块的类路径顺序。即,在WAS控制台下打开war应用程序,然后选择客户端模块。在模块配置中,将类加载设置为“最后一个父级”。
在WAS控制台中可以找到:
由于某些原因,使用JNI并在调用a时传递jclass参数而不是jobject时,也会引发相同的异常Call*Method()
。
这类似于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
因此我想记录这个特殊情况。