Java URLClassLoader无法识别字符串

4

我遇到了一个非常奇怪的问题。

我正在使用URLClassLoader从目录动态导入文件。如果我使用字面字符串,代码可以正常工作;如果我使用指向字面字符串的变量,也可以正常工作,但这不是我需要的。

package test;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

public class Test {
    public static void main(String[] args) {

        try {
            File subfolder = new File("C:\\temp\\");
            URL classUrl = subfolder.toURI().toURL();
            URL[] classUrls = { classUrl };
            URLClassLoader ucl = new URLClassLoader(classUrls);

            for (File f : subfolder.listFiles()) {

                String name = f.getName()
                        .substring(0, f.getName().lastIndexOf(".")).trim();
                if (name.equals("TestClass"))
                        System.out.println(name);
                try {
                    MyInterface de = (MyInterface) Class.forName("TestClass", true, ucl)
                            .newInstance();
                    de.printSomething();
                } catch (ClassNotFoundException e) {
                }

                ucl.close();

            }
        } catch (MalformedURLException e) {
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

我需要能够做到这一点:
MyInterface de = (MyInterface) Class.forName(name, true, ucl).newInstance();

但是即使“name”是有效的字符串并且确实等于“TestClass”,它仍然无法正常工作。
编辑:我收到了错误消息:
java.lang.ClassNotFoundException: TestClass
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Unknown Source)
    at test.Test.main(Test.java:25)

有什么问题吗?


你确定文件夹c:\temp中存在TestClass.class吗?因为看起来你在该目录下有TestClass.java而不是TestClass.class,你还没有测试文件名是否以.class字符串结尾。 - Vishal K
如果上面的代码完美运行,那么问题是什么? - user207421
它可以使用字面字符串 "TestClass",但无法使用字符串变量。正如问题所述。 - Apache
你说“如果我使用变量到一个字符串字面值,它可以正常工作”,我猜这是打错了吧? - Zarwan
@Apache 展示一个能够重现问题的代码示例 - 正如其中一条回答所详细说明的那样,您展示的代码不可能导致该错误。我建议使用类似以下代码:String name = "TestClass"; Class.forName("TestClass", true, ucl).newInstance(); Class.forName(name, true, ucl).newInstance(); 不要捕获异常 - 如果前面的代码没有抛出异常,我非常怀疑最后一行会抛出异常... - assylias
显示剩余3条评论
4个回答

4

看起来你的问题与你加载的类有一个包有关。当Java加载这些类时,它期望找到与该包相关的目录结构。因此,以下代码有效:

try {
        File subfolder = new File("/home/glen/TestClass");
        URL classUrl = subfolder.toURI().toURL();
        URL[] classUrls = { classUrl };
        URLClassLoader ucl = new URLClassLoader(classUrls);

        for (File f : subfolder.listFiles()[0].listFiles()) {

            System.out.println(f.getName());

            String name = f.getName()
                    .substring(0, f.getName().lastIndexOf(".")).trim();// "TestClass";
            if (name.equals("TestClass"))
                    System.out.println(name);
            try {
                MyInterface de = (MyInterface) Class.forName("test." + name, true, ucl)
                        .newInstance();
                de.printSomething();
            } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
            }

            ucl.close();

        }
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

请注意以下几点:
  1. 在`Class.forName`方法中指定完整的类名,即`test.TestClass`
  2. 枚举加载文件夹的第一个子目录(在我的情况下是“test”目录),TestClass.class文件位于其中。如果我直接尝试加载该目录,无论是否指定了包名称,都会失败。
实质上,URLClassLoader需要像`test/TestClass.class`这样的类似JAR的目录结构,并且具有包含目录结构的URL根路径。
我的工作理论是:您不仅仅将名称变量更改为字符串字面量,因为当我这样做时,它仍然可以正常工作。请再次确认您没有更改任何其他内容。无论如何,我希望这将引导您走向正确的方向。

4

我猜这是由于for循环中的ucl.close()引起的。如果存在目录,我添加了一些测试:如果在根包中声明并且eclipse配置为在“bin”目录中生成“.class”文件,则以下类可以工作并实例化自身:

public class Toto {

    public Toto(){
        // this writes "Toto" in the console if the class is well instanciated
        System.out.println("Toto");
    }

    public static void main(String[] args) {
        try {
            File subfolder = new File("bin");
            URL classUrl = subfolder.toURI().toURL();
            URL[] classUrls = { classUrl };
            URLClassLoader ucl = new URLClassLoader(classUrls);

            for (File f : subfolder.listFiles()) {
                String fileName= f.getName();
                int suffix = fileName.lastIndexOf('.');
                if(f.isDirectory() || suffix==-1){
                    continue;
                }
                String name = fileName.substring(0, suffix);
                try {
                    Class.forName(name, true, ucl).newInstance();

                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
            ucl.close();
        } catch (MalformedURLException e) {
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3

出了什么问题?

首先,这不是真正的代码。这段代码捕获ClassNotFoundException并忽略它,这是有问题的:

} catch (ClassNotFoundException e) {
}

因此,它不可能产生所示的输出。

第二个问题是当纠正后,代码按预期工作。

无法再现。

显然,你没有运行你认为你在运行的代码。Java根本不像所声称的那样运行。


2

不必再

MyInterface de = (MyInterface) Class.forName(name, true, ucl).newInstance();

使用
MyInterface de = (MyInterface) Class.forName(name + "Class", true, ucl).newInstance();

适用于我的情况。


1
只有当 name = "Test",且 OP 明确指出它是 "TestClass" 时,才能理解你测试的内容。但是由于他的代码和 Java 行为与描述不符,因此也无法理解 OP 正在测试什么。 - user207421

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接