How to avoid NoClassDefFoundError thrown by unused code in Java(如何避免Java中未使用的代码抛出NoClassDefFoundError)
问题描述
我正在进行的项目是一个支持两个不同平台的 API.在运行时,类路径中实际上只有两个平台中的一个可用.
The project I am working on is an API to support two different platforms. At runtime only one of the two platforms will actually be available on the classpath.
在大多数情况下,我很容易就能编写出这样运行良好的代码
For the most part, I have been pretty easily able to write code like this that works fine
if (isPlatformOne()) {
PlatformOne.doSomething();
}
即使 PlatformOne
在运行时不存在,预先检查也意味着代码不会运行,不会抛出错误.这种技术适用于 VAST 的大多数情况,但是我遇到过一种情况会引发错误.
Even if PlatformOne
does not exist at runtime, the check beforehand means the code does not run and no error will be thrown. This technique works for the VAST majority of situations however there is one case that I have run into where an error is thrown.
如果 PlatformOne
还实现了一个不存在的接口 AND 与 ALSO 不存在的参数一起使用,则在加载包含类时立即抛出 NoClassDefFoundError
,无论代码是否实际执行.
If PlatformOne
also implements a nonexistent interface AND that is used with a parameter that ALSO does not exist, then a NoClassDefFoundError
is thrown immediately when the containing class is loaded, regardless of whether the code actually executes or not.
这是一个例子:
界面:
public interface DeleteInterface {
void test(DeleteInterface delete);
}
类:
public class DeleteClass implements DeleteInterface {
@Override
public void test(DeleteInterface delete) {
}
}
主要:
public class Test {
private final boolean test; //Cannot be constant or compiler will erase unreachable code
public Test() {
test = false;
}
public static void main(String[] args) {
if (new Test().test) {
DeleteClass c = new DeleteClass();
c.test(c);
}
System.out.println("SUCCESS!");
}
}
从 jar 中删除 DeleteClass
和 DeleteInterface
会在运行时产生以下错误:
Deleting DeleteClass
and DeleteInterface
from the jar produces the following error at runtime:
A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.NoClassDefFoundError: com/kmecpp/test/DeleteInterface
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
at java.lang.Class.getMethod0(Class.java:3018)
at java.lang.Class.getMethod(Class.java:1784)
at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544)
at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526)
Caused by: java.lang.ClassNotFoundException: com.kmecpp.test.DeleteInterface
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 7 more
为什么只针对这种特定情况抛出错误,以及在不访问任何目标平台代码的情况下解决该错误的最佳方法是什么?
Why is an error thrown only for this specific case, and what's the best way to work around it without access to any of the target platforms' code?
推荐答案
Java 验证器可能会在完全加载你的类之前抛出 NoClassDefFoundError
因为额外的验证,比如方法返回类型必须存在,另外你是正如您在堆栈跟踪中看到的那样,在 JRE 在启动时扫描的 Main 类中执行此操作.
将需要不存在代码的代码移动到其他类,然后在您要使用它的地方首先检查该类是否存在,然后从该额外类调用方法:
Java validator might throw NoClassDefFoundError
before even fully loading your class because of additional validations, like method return types must exist, additionally you are doing that in your Main class that is scanned by JRE on launch as you can see in stack-trace.
Move code that requires not-existing code to other class and then in place where you want to use it first check if that class exist and then invoke method from that extra class:
class MyExtraClass {
public static void doStuff() {
DeleteClass c = new DeleteClass();
c.test(c);
}
}
public boolean checkForClass(String className) {
try {
Class.forName(className);
return true;
} catch (ClassNotFoundException e) { return false; }
}
// somewhere in your code
if (checkForClass("package.DeletedClass")) {
MyExtraClass.doStuff();
}
这只是这种情况下最安全的选择,如果这是非常短的代码,您可以只使用一些本地类:(但在大多数情况下看起来并不好)
This is just safest option for such cases, also if this is very short code you can just use some local class: (but it does not look good in most cases)
// somewhere in your code
if (checkForClass("package.DeletedClass")) {
new Runnable() {
@Override public void run() {
DeleteClass c = new DeleteClass();
c.test(c);
}.run();
}
}
这篇关于如何避免Java中未使用的代码抛出NoClassDefFoundError的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:如何避免Java中未使用的代码抛出NoClassDefFoundError
基础教程推荐
- 首次使用 Hadoop,MapReduce Job 不运行 Reduce Phase 2022-01-01
- 如何使用 Stream 在集合中拆分奇数和偶数以及两者的总和 2022-01-01
- 如何对 HashSet 进行排序? 2022-01-01
- 如何在不安装整个 WTP 包的情况下将 Tomcat 8 添加到 Eclipse Kepler 2022-01-01
- 在螺旋中写一个字符串 2022-01-01
- 如何使用 Eclipse 检查调试符号状态? 2022-01-01
- 如何强制对超级方法进行多态调用? 2022-01-01
- Spring Boot Freemarker从2.2.0升级失败 2022-01-01
- 由于对所需库 rt.jar 的限制,对类的访问限制? 2022-01-01
- Java 中保存最后 N 个元素的大小受限队列 2022-01-01