是什么原因导致 java.lang.IncompleteClassChangeError?
我将 Java 库打包为 JAR,当我尝试从中调用方法时,它会抛出许多 java.lang.IncompleteClassChangeError 错误。这些错误似乎是随机出现的。哪些类型的问题可能导致此错误?
I'm packaging a Java library as a JAR, and it's throwing many java.lang.IncompatibleClassChangeError
s when I try to invoke methods from it. These errors seem to appear at random. What kinds of problems could be causing this error?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(21)
我相信,我的答案将是 Intellij 具体的。
我已经重建干净,甚至手动删除“out”和“target”目录。 Intellij 有一个“使缓存无效并重新启动”功能,有时可以清除奇怪的错误。这次没有成功。依赖项版本在项目设置->模块菜单中看起来都是正确的。
最终的答案是从本地 Maven 存储库中手动删除我的问题依赖项。旧版本的 bouncycastle 是罪魁祸首(我知道我刚刚更改了版本,这就是问题所在),尽管旧版本在正在构建的内容中没有出现,但它解决了我的问题。我使用的是intellij版本14,然后在此过程中升级到15。
My answer, I believe, will be Intellij specific.
I had rebuilt clean, even going as far as to manually delete the "out" and "target" dirs. Intellij has a "invalidate caches and restart", which sometimes clears odd errors. This time it didn't work. The dependency versions all looked correct in the project settings->modules menu.
The final answer was to manually delete my problem dependency from my local maven repo. An old version of bouncycastle was the culprit(I knew I had just changed versions and that would be the problem) and although the old version showed up no where in what was being built, it solved my problem. I was using intellij version 14 and then upgraded to 15 during this process.
导致此问题的另一个原因是您是否为 Android Studio 启用了
Instant Run
。解决方法
如果您发现开始出现此错误,请关闭
Instant Run
。为什么
Instant Run
在开发过程中修改大量内容,以便更快地为正在运行的 App 提供更新。因此立即运行。当它起作用时,它确实很有用。但是,当出现此类问题时,最好的办法是关闭Instant Run
,直到下一个版本的 Android Studio 发布为止。An additional cause of this issue, is if you have
Instant Run
enabled for Android Studio.The fix
If you find you start getting this error, turn off
Instant Run
.Why
Instant Run
modifies a large number of things during development, to make it quicker to provide updates to your running App. Hence instant run. When it works, it is really useful. However, when an issue such as this strikes, the best thing to do is to turn offInstant Run
until the next version of Android Studio releases.我得到这个错误是因为我有一个抽象基类,它承诺它实现某个接口,但我忘记添加接口方法的实现,然后我创建了一个非抽象(具体)字节码生成的类,它扩展了抽象类,也不提供这些方法的实现。
当我尝试创建字节码生成的类的实例时,JVM 抱怨
java.lang.InknownClassChangeError
。幸运的是,异常有一个“消息”成员,它提供了有关出错原因的更详细信息。就我而言,该消息清楚地表明特定的类应该实现特定的接口,但它实际上并未实现它。
I got this error because I had an abstract base class which promised that it implements a certain interface, but I had forgotten to add the implementations of the interface methods, and then I created a non-abstract (concrete) bytecode-generated class which extended the abstract class, without providing implementations for those methods, either.
When I tried to create an instance the bytecode-generated class, the JVM complained with
java.lang.IncompatibleClassChangeError
.Luckily, the exception has a "message" member which provides more detailed information as to what went wrong. In my case the message clearly said that the particular class was supposed to implement the particular interface, but it did not actually implement it.
请检查您的代码是否不包含两个具有相同类名称和包定义的模块项目。例如,如果有人使用复制粘贴基于先前的实现创建新的接口实现,则可能会发生这种情况。
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.
如果这是此错误可能发生的记录,那么:
我刚刚在 CXF (2.6.0) 加载 spring (3.1.1_release) 配置期间在 WAS (8.5.0.1) 上收到此错误,其中 BeanInstantiationException 汇总了CXF ExtensionException,汇总了 IncompleteClassChangeError。以下代码片段显示了堆栈跟踪的要点:
在本例中,解决方案是更改 war 文件中模块的类路径顺序。也就是说,在 WAS 控制台中打开 war 应用程序并选择客户端模块。在模块配置中,将类加载设置为“parent last”。
这可以在 WAS 控制台中找到:
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:
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".
This is found in the WAS console:
在花费太多时间后记录另一个场景。
确保您的依赖项 jar 中没有带有 EJB 注释的类。
我们有一个带有
@local
注释的公共 jar 文件。该类后来从该公共项目中移出并移入我们的主 ejb jar 项目中。我们的 ejb jar 和普通 jar 都捆绑在一个耳朵内。我们常用的jar依赖的版本没有更新。因此,两个类试图进行不兼容的更改。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.
We had a common jar file that had an
@local
annotation. 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.由于某种原因,当使用 JNI 并在调用 Call*Method() 时传递 jclass 参数而不是 jobject 时,也会引发相同的异常。
这与 Ogre Psalm33 的答案类似。
我知道在被问到这个问题 5 年后回答这个问题有点晚了,但这是搜索
java.lang.InknownClassChangeError
时最热门的搜索结果之一,所以我想记录这个特殊情况。For some reason the same exception is also thrown when using JNI and passing the jclass argument instead of the jobject when calling a
Call*Method()
.This is similar to the answer from Ogre Psalm33.
I know it is a bit late to answer this question 5 years after being asked but this is one of the top hits when searching for
java.lang.IncompatibleClassChangeError
so I wanted to document this special case.添加我的 2 美分。如果您使用 scala 和 sbt 以及 scala-logging 作为依赖项;那么这种情况可能会发生,因为 scala-logging 的早期版本名为 scala-logging-api。所以;本质上,由于不同的原因,依赖项解析不会发生启动 scala 应用程序时导致运行时错误的名称。
Adding my 2 cents .If you are using scala and sbt and scala-logging as dependency ;then this can happen because scala-logging's earlier version had the name scala-logging-api.So;essentially the dependency resolutions do not happen because of different names leading to runtime errors while launching the scala application.
如果你来自android开发。然后尝试一下
rebuild
选项可能会解决你的问题。If you came from android development. Then give a try of
rebuild
option might be fix for you.就我而言:
app
、test
、integrationTest
OneElementCache
在应用程序模块中。Cache
,该文件包含一些用于在测试中创建OneElementCache
的帮助程序。test
和integrationTest
均通过)。Cache
。integrationTest
时得到:原因是不同模块(应用程序/测试)中的命名冲突。更改测试中的文件名即可完成工作。
In my case:
app
,test
,integrationTest
OneElementCache
in app module.Cache
in test module, the file contains some helpers for creatingOneElementCache
in tests.test
andintegrationTest
passes).Cache
in app module.integrationTest
:The reason was a conflict in naming in different modules (app/test). Changing the filename in test did the job.
这意味着您对库进行了一些不兼容的二进制更改,而无需重新编译客户端代码。 Java 语言规范 §13 详细介绍了所有此类更改最突出的是,将非
静态
非私有字段/方法更改为静态
,反之亦然。针对新库重新编译客户端代码,然后就可以开始了。
更新:如果您发布公共库,您应该尽可能避免进行不兼容的二进制更改,以保留所谓的“二进制向后兼容性”。理想情况下,单独更新依赖项 jar 不应破坏应用程序或构建。如果您确实必须破坏二进制向后兼容性,建议增加主版本号(例如从 1.xy 到 2.0 .0) 在发布更改之前。
This means that you have made some incompatible binary changes to the library without recompiling the client code. Java Language Specification §13 details all such changes, most prominently, changing non-
static
non-private fields/methods to bestatic
or vice versa.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 recommended to increase the major version number (e.g. from 1.x.y to 2.0.0) before releasing the change.
您新打包的库不向后二进制兼容(BC)旧版本。因此,一些未重新编译的库客户端可能会抛出异常。
这是 Java 库 API 中的更改的完整列表,这些更改可能会导致使用旧版本库构建的客户端在新版本上运行时抛出 java.lang.IncompleteClassChangeError一(即打破BC):
注意:还有许多其他异常是由其他不兼容的更改引起的:NoSuchFieldError、NoSuchMethodError、IllegalAccessError< /em>、InstantiationError、VerifyError、NoClassDefFoundError 和 AbstractMethodError。
关于 BC 的更好的论文是 “发展基于 Java 的 API 2:实现 API 二进制兼容性”,作者:吉姆·德·里维埃。
还有一些自动工具来检测此类更改:
在您的库中使用 japi-compliance-checker:
使用 clirr 工具:
祝你好运!
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.
This is a complete list of changes in Java library API that may cause clients built with an old version of the library to throw java.lang.IncompatibleClassChangeError if they run on a new one (i.e. breaking BC):
Note: There are many other exceptions caused by other incompatible changes: NoSuchFieldError, NoSuchMethodError, IllegalAccessError, InstantiationError, VerifyError, NoClassDefFoundError and AbstractMethodError.
The better paper about BC is "Evolving Java-based APIs 2: Achieving API Binary Compatibility" written by Jim des Rivières.
There are also some automatic tools to detect such changes:
Usage of japi-compliance-checker for your library:
Usage of clirr tool:
Good luck!
虽然这些答案都是正确的,但解决问题往往更加困难。它通常是类路径上相同依赖项的两个稍微不同的版本的结果,并且几乎总是由与最初针对类路径上编译的超类不同的超类或传递闭包的某些导入引起有所不同,但通常在类实例化和构造函数调用时。 (在成功的类加载和构造函数调用之后,您将得到
NoSuchMethodException
或诸如此类的信息。)如果行为出现随机,则可能是多线程程序根据首先命中的代码加载不同传递依赖项的结果。
要解决这些问题,请尝试使用
-verbose
作为参数启动 VM,然后查看发生异常时正在加载的类。您应该会看到一些令人惊讶的信息。例如,拥有相同依赖项和版本的多个副本,如果您知道它们被包含在内,您从未预料到或会接受它们。使用 Maven 解决重复的 jar 问题最好结合 maven-dependency-plugin 和 Maven 下的 maven-enforcer-plugin (或 SBT 的 依赖关系图插件,然后将这些 jar 添加到顶级 POM 的一部分或作为导入的依赖元素 中(删除这些依赖项)。
在SBT
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.)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
-verbose
as 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.Resolving duplicate jars with Maven is best done with a combination of the maven-dependency-plugin and maven-enforcer-plugin under 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).
Good luck!
我还发现,当使用 JNI 从 C++ 调用 Java 方法时,如果以错误的顺序将参数传递给调用的 Java 方法,则当您尝试在被调用方法中使用参数时,您将收到此错误(因为它们不会是正确的类型)。最初让我感到惊讶的是,当您调用该方法时,JNI 不会为您执行此检查作为类签名检查的一部分,但我认为它们不会执行此类检查,因为您可能传递多态参数,并且它们必须假设你知道自己在做什么。
C++ JNI 代码示例:
Java 代码示例:
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.
Example C++ JNI Code:
Example Java Code:
我也遇到了同样的问题,后来我发现我在Java版本1.4上运行应用程序,而应用程序是在版本6上编译的。实际上,原因是有一个重复的库,一个是位于类路径中,另一个包含在位于类路径中的 jar 文件中。
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.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.
就我而言,我以这种方式遇到了这个错误。我的项目的
pom.xml
定义了两个依赖项A
和B
。A
和B
都定义了对同一工件(称为C
)的依赖,但它的版本不同(C.1和<代码>C.2)。发生这种情况时,对于
C
中的每个类,maven 只能从两个版本中选择该类的一个版本(同时构建 uber-jar)。它将根据其 依赖中介< /a> 规则并将输出警告 “我们有一个重复的类。 .." 如果方法/类签名在版本之间发生更改,并且在运行时使用了不正确的版本,则可能会导致java.lang.InknownClassChangeError
异常。高级:如果
A
必须使用C
的v1并且B
必须使用C
的v2,那么我们必须重新定位C
A
和B
的 pom 以避免在构建依赖于A
和A
的最终项目时发生类冲突(我们有重复的类警告)B
。In my case, I ran into this error this way.
pom.xml
of my project defined two dependenciesA
andB
. And bothA
andB
defined dependency on same artifact (call itC
) but different versions of it (C.1
andC.2
). When this happens, for each class inC
maven can only select one version of the class from the two versions (while building an uber-jar). It will select the "nearest" version based on its dependency mediation rules and will output a warning "We have a duplicate class..." If a method/class signature changes between the versions, it can cause ajava.lang.IncompatibleClassChangeError
exception if the incorrect version is used at runtime.Advanced: If
A
must use v1 ofC
andB
must use v2 ofC
, then we must relocateC
inA
andB
's poms to avoid class conflict (we have a duplicate class warning) when building the final project that depends on bothA
andB
.就我而言,当我在部署在
Websphere 8.5
上的应用程序中添加com.nimbusds
库时,出现了错误。发生以下异常:
解决方案是从库中排除 asm jar:
In my case the error appeared when I added the
com.nimbusds
library in my application deployed onWebsphere 8.5
.The below exception occurred:
The solution was to exclude the asm jar from the library:
可能出现此错误的另一种情况是 Emma 代码覆盖率。
当将对象分配给接口时会发生这种情况。我想这与正在检测的对象有关,并且不再与二进制兼容。
http://sourceforge.net/tracker/?func =detail&aid=3178921&group_id=177969&atid=883351
幸运的是,Cobertura 不会出现这个问题,所以我在 pom.xml 的报告插件中添加了 cobertura-maven-plugin
Another situation where this error can appear is with 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
Fortunately this problem doesn't happen with Cobertura, so I've added cobertura-maven-plugin in my reporting plugins of my pom.xml
我在取消部署和重新部署与 glassfish 的战争时遇到了这个问题。我的类结构是这样的,
并将其更改为
停止并重新启动域后,效果很好。
我使用的是 glassfish 3.1.43
I've faced this issue while undeploying and redeploying a war with glassfish. My class structure was like this,
and it was changed to
After stopping and restarting the domain, it worked out fine.
I was using glassfish 3.1.43
以上所有内容 - 无论出于何种原因,我都进行了一些大型重构并开始了解这一点。我重命名了我的界面所在的包并清除了它。希望有帮助。
All of the above - for whatever reason I was doing some big refactor and starting to get this. I renamed the package my interface was in and that cleared it. Hope that helps.
我有一个 Web 应用程序,可以在本地计算机的 tomcat(8.0.20) 上完美部署。然而,当我将它放入 qa 环境(tomcat - 8.0.20)时,它不断地给我 IncompleteClassChangeError 并抱怨我正在接口上扩展。该接口已更改为抽象类。我编译了家长和孩子的课程,但我仍然遇到同样的问题。最后,我想调试,所以,我将父级上的版本更改为 x.0.1-SNAPSHOT,然后编译所有内容,现在它可以工作了。如果有人在遵循此处给出的答案后仍然遇到问题,请确保 pom.xml 中的版本也正确。更改版本以查看是否有效。如果是,则修复版本问题。
I have a web application that deploys perfectly fine on my local machine's tomcat(8.0.20). However, when I put it into the qa environment (tomcat - 8.0.20), it kept on giving me the IncompatibleClassChangeError and it was complaining that I was extending on an interface. This interface was changed to an abstract class. And I compiled the parent and child classes and still I kept on getting the same issue. Finally, I wanted to debug, so, I changed the version on the parent to x.0.1-SNAPSHOT and then compiled everything and now it is working. If someone is still hitting the problem after following the answers given here, please make sure the versions in your pom.xml are also correct. Change the versions to see if that works. If so, then fix the version problem.