使用URLClassLoader加载类时,继续获取NoClassDefFoundError

时间:2015-12-28 12:04:56

标签: java classnotfoundexception urlclassloader

最近我创建了一些必须动态加载/卸载外部jar包的东西。我现在尝试使用URLClassLoader执行此操作,但在尝试创建新实例时,我一直收到NoClassDefFoundError

由于构造函数中的代码已执行,似乎外部类已成功加载,但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包中,它在启动时包含在类路径中。 我在控制台中得到了这个: Console output screenshot

当执行this.print()的两个构造函数中的代码ExternalObject.class时,我真的不知道错误是什么。救命! :(

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

此致 泽文

4 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

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

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

uclTest类加载器从ExternalObject成功加载ext.jar。创建也成功(由构造函数中的print语句看到)。

但失败的是对局部变量ExternalObject extObj的分配。 Main无法使用加载的类ExternalObject,因为它由不同的类加载器加载。 ExternalObject的类路径中也没有Main,您将获得NoClassDefFoundError

删除两个作业ExternalObject extObj = (ExternalObject)时,您的测试应该没有问题。

答案 2 :(得分:0)

如何运行Main类导致问题。

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

如果我运行以下命令,它会抛出异常,就像你提到的那样

  

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

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

  

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

答案 3 :(得分:0)

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