如何避免 Java 中未使用的代码抛出 NoClassDefFoundError
How to avoid NoClassDefFoundError thrown by unused code in Java
我正在从事的项目是 API 以支持两个不同的平台。在 运行 时间,class 路径上实际上只有两个平台之一可用。
在大多数情况下,我很容易就能编写出像这样运行良好的代码
if (isPlatformOne()) {
PlatformOne.doSomething();
}
即使PlatformOne
在运行时不存在,事先检查意味着代码不运行并且不会抛出错误。这种技术适用于大多数情况,但有一种情况 运行 会引发错误。
如果 PlatformOne
也实现了一个不存在的接口 AND 并且与一个不存在的参数一起使用,那么当包含 class 被加载时会立即抛出 NoClassDefFoundError
,不管代码是否实际执行。
举个例子:
接口:
public interface DeleteInterface {
void test(DeleteInterface delete);
}
Class:
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
为什么只针对这种特定情况抛出错误,在不访问任何目标平台代码的情况下解决该错误的最佳方法是什么?
实际上我今天遇到了这个问题。
确保您没有在系统 class 加载程序中加载相同的 class 两次。
I.E) 我在前端线程中引用了 a.b.class,我试图引用具有相同路径和 class 名称的库方法,因此抛出对我来说同样的错误。
我将代理引用中的名称更改为与前端引用不同,错误停止了。
希望这对您有所帮助
Deleting DeleteClass and DeleteInterface from the jar produces the
following error at runtime:
如果需要的class在运行时不存在,肯定,java.lang.NoClassDefFoundError
会被抛出。
Even if PlatformOne does not exist at runtime, the check beforehand
means the code does not run and no error will be thrown.
请检查您的代码是否消化了抛出的错误,如果是,您的应用将不会崩溃并且可以正常执行。例如。下面的代码片段将抛出 NoClassDefFoundError
但不会崩溃,因为您消化了错误。
public bool isPlatformOne() {
try {
...
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
如果您的用例只是检查特定 class 是否存在,那么您可以使用 Class.forName
来检查 class 是否存在。例如。
// className is the fully qualified class name.
public boolean hasClass(String className) {
try {
Class.forName(className);
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
在代码中使用它的示例。
if (hasClass("android.support.v7.app.AppCompatActivity")) {
...
}
Java 验证器可能会在完全加载你的 class 之前抛出 NoClassDefFoundError
因为额外的验证,比如方法 return 类型必须存在,此外你正在这样做正如您在堆栈跟踪中看到的那样,您的 Main class 在启动时由 JRE 扫描。
将需要不存在代码的代码移动到其他 class,然后在您要使用它的地方首先检查 class 是否存在,然后从那个额外的 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();
}
对于这种情况,这是最安全的选择,如果这是非常短的代码,您也可以使用一些本地 class:(但在大多数情况下看起来不太好)
// somewhere in your code
if (checkForClass("package.DeletedClass")) {
new Runnable() {
@Override public void run() {
DeleteClass c = new DeleteClass();
c.test(c);
}.run();
}
}
我正在从事的项目是 API 以支持两个不同的平台。在 运行 时间,class 路径上实际上只有两个平台之一可用。
在大多数情况下,我很容易就能编写出像这样运行良好的代码
if (isPlatformOne()) {
PlatformOne.doSomething();
}
即使PlatformOne
在运行时不存在,事先检查意味着代码不运行并且不会抛出错误。这种技术适用于大多数情况,但有一种情况 运行 会引发错误。
如果 PlatformOne
也实现了一个不存在的接口 AND 并且与一个不存在的参数一起使用,那么当包含 class 被加载时会立即抛出 NoClassDefFoundError
,不管代码是否实际执行。
举个例子:
接口:
public interface DeleteInterface {
void test(DeleteInterface delete);
}
Class:
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
为什么只针对这种特定情况抛出错误,在不访问任何目标平台代码的情况下解决该错误的最佳方法是什么?
实际上我今天遇到了这个问题。
确保您没有在系统 class 加载程序中加载相同的 class 两次。
I.E) 我在前端线程中引用了 a.b.class,我试图引用具有相同路径和 class 名称的库方法,因此抛出对我来说同样的错误。
我将代理引用中的名称更改为与前端引用不同,错误停止了。
希望这对您有所帮助
Deleting DeleteClass and DeleteInterface from the jar produces the following error at runtime:
如果需要的class在运行时不存在,肯定,java.lang.NoClassDefFoundError
会被抛出。
Even if PlatformOne does not exist at runtime, the check beforehand means the code does not run and no error will be thrown.
请检查您的代码是否消化了抛出的错误,如果是,您的应用将不会崩溃并且可以正常执行。例如。下面的代码片段将抛出 NoClassDefFoundError
但不会崩溃,因为您消化了错误。
public bool isPlatformOne() {
try {
...
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
如果您的用例只是检查特定 class 是否存在,那么您可以使用 Class.forName
来检查 class 是否存在。例如。
// className is the fully qualified class name.
public boolean hasClass(String className) {
try {
Class.forName(className);
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
在代码中使用它的示例。
if (hasClass("android.support.v7.app.AppCompatActivity")) {
...
}
Java 验证器可能会在完全加载你的 class 之前抛出 NoClassDefFoundError
因为额外的验证,比如方法 return 类型必须存在,此外你正在这样做正如您在堆栈跟踪中看到的那样,您的 Main class 在启动时由 JRE 扫描。
将需要不存在代码的代码移动到其他 class,然后在您要使用它的地方首先检查 class 是否存在,然后从那个额外的 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();
}
对于这种情况,这是最安全的选择,如果这是非常短的代码,您也可以使用一些本地 class:(但在大多数情况下看起来不太好)
// somewhere in your code
if (checkForClass("package.DeletedClass")) {
new Runnable() {
@Override public void run() {
DeleteClass c = new DeleteClass();
c.test(c);
}.run();
}
}