如何避免Java中未使用的代码引发NoClassDefFoundError [英] How to avoid NoClassDefFoundError thrown by unused code in Java

查看:93
本文介绍了如何避免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 pretty easily been able to write code like this that works fine

if(isPlatformOne(){
    PlatformOne.doSomething();
}

即使 PlatformOne 在运行时不存在,事先检查也意味着此代码适用于大多数情况,但是在某些情况下我遇到了抛出错误的情况。

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 还实现了一个不存在的接口,并且该接口与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) {
    }

}

Main:

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会对其进行扫描,如在堆栈跟踪中所见。

将不需要代码的代码移至其他类,然后将其放置在所需的位置使用它首先检查该类是否存在,然后从该额外的类中调用方法:

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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆