使用 URLClassLoader 加载 class 时不断出现 NoClassDefFoundError

Keep getting NoClassDefFoundError while loading a class with URLClassLoader

最近我正在创建一些必须动态地 load/unload 外部 jar 包的东西。我现在正尝试使用 URLClassLoader 执行此操作,但在尝试创建新实例时我不断收到 NoClassDefFoundError

似乎外部class加载成功,因为构造函数中的代码被执行了,但是ClassNotFoundExceptionNoClassDefFoundError仍然继续抛出。

我做了一个小包来重现错误,代码如下:

下面的代码在 ExternalObject.class 中,它被放在一个 .jar 文件中,我正在尝试动态加载它:

package test.outside;

import test.inside.InternalObject;

public class ExternalObject
{
    private final String str;

    public ExternalObject()
    {
        this.str = "Creating an ExternalObject with nothing.";
        this.print();
    }

    public ExternalObject(InternalObject inObj)
    {
        this.str = inObj.getString();
        this.print();
    }

    public void print()
    {
        System.out.println(this.str);
    }
}

下面的代码在InternalObject.class:

package test.inside;

public class InternalObject
{
    private final String str;

    public InternalObject(String s)
    {
        this.str = s;
    }

    public String getString()
    {
        return this.str;
    }
}

我用下面的 Main.class 测试了文件:

package test.inside;

import java.io.File;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLClassLoader;

import test.outside.ExternalObject;

public class Main
{
    public static void main(String[] args)
    {
        try
        {
            File externalJar = new File("F:\Dev\ext.jar");
            URLClassLoader uclTest = new URLClassLoader(new URL[]{externalJar.toURI().toURL()});
            Class<?> clazz = uclTest.loadClass("test.outside.ExternalObject");

            InternalObject inObj = new InternalObject("Creating an ExternalObject with an InternalObject.");

            try
            {
                System.out.println("Test 1: Attempt to create an instance of the ExternalObject.class with an InternalObject in the constructor.");
                Constructor<?> conTest = clazz.getConstructor(InternalObject.class);
                ExternalObject extObj = (ExternalObject)conTest.newInstance(inObj);
            }
            catch(Throwable t)
            {
                System.out.println("Test 1 has failed. :(");
                t.printStackTrace();
            }

            System.out.println();

            try
            {
                System.out.println("Test 2: Attempt to create an instance of the ExternalObject.class with a void constructor.");
                Constructor<?> conTest = clazz.getConstructor();
                ExternalObject extObj = (ExternalObject)conTest.newInstance();
            }
            catch(Throwable t)
            {
                System.out.println("Test 2 has failed. :(");
                t.printStackTrace();
            }

            uclTest.close();
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }
}

InternalObject.classMain.class 都在一个 jar 包中,启动时包含在 class 路径中。 我在控制台中得到了这个: Console output screenshot

由于 ExternalObject.class 的两个构造函数中的代码 this.print() 都被执行了,我真的不知道出了什么问题。帮助! :(

更新:谢谢你!!!但我实际上想创建一个 ExternalObject 的实例以供进一步使用,例如从其他 classes 访问其中的方法。有什么方法可以 return 创建的实例作为 ExternalObject 吗?或者我必须使用 getMethod()invoke() 来访问这些方法?

真诚的, 泽文

我认为是因为涉及两个 classLoader,并且您尝试将一个对象从一个 classLoader 转换为另一个 class 加载器的对象。但只是猜测。

您的 Main class 引用 ExternalObject 因此编译的 Main.class 依赖于 ExternalObject.

现在,当您 运行 MainExternalObject 仅在 ext.jar 中可用但在用于 运行 的 class 路径中不可用时Main 发生以下情况:

uclTest class 加载程序成功从 ext.jar 加载 ExternalObject。同样创建成功(通过构造函数中的打印语句看到)。

但是失败的是对局部变量的赋值ExternalObject extObjMain 无法使用已加载的 class ExternalObject,因为它是由不同的 class 加载程序加载的。 Main 的 class 路径中也没有 ExternalObject,你得到一个 NoClassDefFoundError.

当您删除两个作业 ExternalObject extObj = (ExternalObject)

时,您的测试应该 运行 没有问题

你是如何运行宁主要 class 导致问题。

如您所说,我创建了名为 ext1.jar 的 jar,其中包含 ExternalObject 和 InternalObjct class 文件。 并使用 Main 和 InternalObject class 文件创建了 ext.jar

如果我运行下面的命令,它会抛出你提到的异常

java -classpath .;C: \path\to\ext.jar test.inside.Main

但是,如果我 运行 以下命令,它 运行 没问题,没有任何异常

java -classpath .;C: \path\to\ext1.jar;C: \path\to\ext.jar test.inside.Main

万岁!!我刚刚为我的代码找到了更好的方法!我所做的是用我需要的所有抽象方法创建一个 abstract class ExternalBase.class,然后从 ExternalBase.class 继承 ExternalObject.class。因此,动态加载的 class 既不必加载到自定义加载器中,也不必由使用该对象的 classes 导入,代码对我来说非常完美。 :)