我将一个Java库打包为JAR,当我试图从它调用方法时,它抛出许多Java .lang. incompatibleclasschangeerror。这些错误似乎是随机出现的。什么样的问题会导致这个错误?


当前回答

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

这发生在将Object分配给接口时。我猜这与对象被检测和不再二进制兼容有关。

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

幸运的是,这个问题在Cobertura中没有发生,所以我在pom.xml的报告插件中添加了Cobertura -maven-plugin

其他回答

在我的例子中:

我有一个项目包含几个模块,包括应用程序,测试,integrationTest 我在app模块中创建了OneElementCache。 然后,我在测试模块中创建了一个文件Cache,该文件包含了一些用于在测试中创建OneElementCache的帮助。 到目前为止,一切都很完美(测试和集成测试都通过了)。 之后,我在app模块中创建了一个文件缓存。 在运行integrationTest时得到:

Caused by: java.lang.IncompatibleClassChangeError: 
    class app.cache.CacheImpl can not implement app.cache.Cache, because it is not an interface (app.cache.Cache is in unnamed module of loader 'app')

原因是不同模块(app/test)的命名冲突。在测试中更改文件名就可以了。

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 or some 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 NoSuchMethodException or whatnot.)

如果这种行为看起来是随机的,那么它很可能是多线程程序类根据最先遇到的代码加载不同的传递依赖项的结果。

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

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

好运!

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

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

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

这与《食人魔诗篇》第33篇中的回答相似。

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时最热门的问题之一,所以我想记录这个特殊情况。

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

这是一个完整的Java库API更改列表,这些更改可能会导致使用旧版本库构建的客户端在运行新版本库时抛出Java .lang. incompatibleclasschangeerror(即破坏BC):

非终场变为静态, 非常数场变成非静态场, 类变成接口, 接口变成类, 如果你添加了一个新的字段到类/接口(或者添加了一个新的超类/超接口),那么一个来自客户端类C的超接口的静态字段可能会隐藏一个从C的超类继承的添加的字段(具有相同的名称)(非常罕见的情况)。

注意:还有许多其他由其他不兼容的更改引起的异常:NoSuchFieldError, NoSuchMethodError, IllegalAccessError, InstantiationError, VerifyError, NoClassDefFoundError和AbstractMethodError。

关于BC更好的论文是由Jim des Rivières撰写的“进化基于java的API 2:实现API二进制兼容性”。

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

japi-compliance-checker clirr japitools sigtest japi-checker

在你的库中使用日语遵从检查器:

japi-compliance-checker OLD.jar NEW.jar

clir工具的使用:

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

好运!