反射如何避免Java中未使用的代码引发NoClassDefFoundError
我正在从事的项目是一个支持两种不同平台的API。在运行时,类路径上实际上只有两个平台中的一个可用
在大多数情况下,我已经非常容易地编写出这样的代码,工作得很好
if (isPlatformOne()) {
PlatformOne.doSomething();
}
即使PlatformOne
在运行时不存在,事先检查意味着代码不会运行,也不会抛出错误。这种技术适用于绝大多数情况,但是我遇到过一个抛出错误的情况
如果PlatformOne
还实现了一个不存在的接口,并且该接口与一个也不存在的参数一起使用,那么在加载包含类时会立即抛出NoClassDefFoundError
,而不管代码是否实际执行
这里有一个例子:
接口:
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
会在运行时产生以下错误:
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
为什么只有在这种特定情况下才会抛出错误,在不访问任何目标平台代码的情况下解决错误的最佳方法是什么
# 1 楼答案
Java验证器可能在完全加载类之前抛出
NoClassDefFoundError
,因为必须存在其他验证,比如方法返回类型,此外,您在主类中这样做,启动时JRE会扫描主类,如堆栈跟踪中所示将不需要现有代码的代码移动到另一个类中,然后移动到要使用它的位置,首先检查该类是否存在,然后从该额外类调用方法:
对于这种情况,这是最安全的选择,而且如果这是非常短的代码,您可以只使用一些本地类:(但在大多数情况下看起来并不好)
# 2 楼答案
如果所需的类在运行时不存在,肯定会抛出
java.lang.NoClassDefFoundError
请检查您的代码是否消化了抛出的错误,如果是,您的应用程序将不会崩溃,并且可以正常执行。例如,下面的代码片段将抛出
NoClassDefFoundError
,但不会崩溃,因为您消化了错误如果您的用例只是检查某个特定类是否存在,那么您可以使用
Class.forName
检查该类是否存在。例如在代码中使用它的示例
# 3 楼答案
事实上,我今天遇到了这个问题
确保没有在系统类装入器中两次装入同一类
也就是说,我在前端线程中引用了一个a.b.class,我试图引用一个具有相同路径和类名的库方法,因此为我抛出了相同的错误
我将代理引用中的名称更改为与前端引用不同,错误停止
希望这能有所帮助