如何避免Java中未使用的代码引发NoClassDefFoundError

时间:2018-07-07 16:06:13

标签: java reflection java-native-interface classloader

我正在研究的项目是一个支持两个不同平台的API。在运行时,类路径中实际上只有两个平台之一。

在大多数情况下,我很容易就能编写出像这样的代码

#include "fenprinc.h"

fenPrinc::fenPrinc()
{
QWidget *zoneC= new QWidget;

setCentralWidget(zoneC);
}

即使if(isPlatformOne(){ PlatformOne.doSomething(); } 在运行时不存在,事先进行的检查也意味着代码不会运行,并且不会引发任何错误。这项技术适用于大多数情况下的VAST,但是有一种情况我遇到了抛出错误的情况。

如果PlatformOne还实现了一个不存在的接口,并且该接口与不存在ALSO的参数一起使用,则在装入包含类时立即抛出PlatformOne,无论代码是否实际执行与否。

以下是一个示例:

接口:

NoClassDefFoundError

班级:

public interface DeleteInterface {

    void test(DeleteInterface delete);

}

主要:

public class DeleteClass implements DeleteInterface {

    @Override
    public void test(DeleteInterface delete) {
    }

}

从jar中删除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!"); } } DeleteClass会在运行时产生以下错误:

DeleteInterface

为什么仅在这种特定情况下会引发错误?在不访问任何目标平台代码的情况下解决该错误的最佳方法是什么?

3 个答案:

答案 0 :(得分:1)

由于额外的验证,Java验证程序甚至可能在完全加载类之前就抛出NoClassDefFoundError,例如必须存在方法返回类型,此外,您正在Main类中执行此操作,JRE会在启动时对其进行扫描,如您所见在堆栈跟踪中。
将不需要代码的代码移至其他类,然后将其移至要使用的位置,首先检查该类是否存在,然后从该额外的类中调用方法:

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();
    }

在这种情况下,这是最安全的选择,如果代码很短,您也可以使用一些本地类:(但在大多数情况下效果并不理想)

// somewhere in your code
    if (checkForClass("package.DeletedClass")) {
        new Runnable() {
            @Override public void run() {
                DeleteClass c = new DeleteClass();
                c.test(c);
            }.run();
        }
    }

答案 1 :(得分:0)

实际上我今天有这个问题。

确保不要在系统类加载器中两次加载同一类。

I.E)我在前端线程中引用了a.b.class,并且试图引用具有相同路径和类名称的库方法,因此对我抛出了相同的错误。

我将代理程序引用中的名称更改为与前端引用不同,并且错误停止了。

希望这会有所帮助

答案 2 :(得分:0)

  

从罐子中删除DeleteClass和DeleteInterface会产生   运行时出现以下错误:

如果所需的类在运行时不存在,请确定,将抛出java.lang.NoClassDefFoundError

  

即使PlatformOne在运行时不存在,也要事先进行检查   表示代码无法运行,也不会引发任何错误。

请检查您的代码是否消化了所引发的错误,如果是,则您的应用不会崩溃,并且可以正常执行。例如。下面的代码段将抛出NoClassDefFoundError,但不会崩溃,因为您可以消化该错误。

public bool isPlatformOne() {
    try  {
        ...
        return true;
    }  catch (ClassNotFoundException e) {
       return false;
    }
}

如果您的用例只是检查特定的类是否存在,则可以使用Class.forName来检查该类的存在。例如。

// 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")) {
    ...
}