如何在 class 路径之外加载 java class?

How to load a java class outside the classpath?

我有一个程序,我希望用户能够从文件系统中选择一个 .java class 文件,然后将该 class 加载到程序中.

我正在使用 JFileChooser 来允许用户 select 一个文件。然后,我尝试将该文件转换为 URL,并使用 URLClassLoader 加载 class(如 these answers 所建议)。

问题是,当我想使用 loadClass() 方法时,我不知道 class 的“class 全名”(例如 java.lang.String).所以,我不知道如何使这种方法起作用。有没有办法得到这个 class 名字?或者还有其他方法吗?

这是我的代码示例:

// Open the file chooser
JFileChooser fileChooser = new JFileChooser();
fileChooser.showOpenDialog(null);
File obtainedFile = fileChooser.getSelectedFile();

// Create the class loader from the file
URL classPath = obtainedFile.toURI().toURL();
URLClassLoader loader = new URLClassLoader(new URL[] {classPath});

// Get the class from the loader
Class<?> theClassIWant = loader.loadClass("the file name");    // What do I put here??

加载单个 class 文件通常完全没用。说 class 文件并不孤单;它有更多 class 个相关文件。即使您认为 'nah, there is just one source file, do not worry about this',请注意单个 java 文件可以轻松生成多个 class 文件。

因此,两个选项:

不要加载 class 个文件。加载 jar 文件。

使用通常的机制(META-INF/servicesMETA-INF/MANIFEST.MF)在其中放置某种 class 名称,以便您知道要加载什么。然后使用提供的 jar 创建一个新的 classloader,加载清单,找出主要的 class,加载它,然后 运行 它。

尝试为加载的 class 文件确定 'root' 并将其包含在 class 路径中。

这很难 - 问题是,对于 'load' 一个 class 文件,你需要告诉加载程序那个 class [=58= 的完全限定名称是什么]before 它被加载。但是你怎么知道完全合格的名字呢?您可以从文件中推测 class 名称(并非总是如此,但通常如此),但包是一个更困难的问题。

您可以自己将 class 文件作为二进制流打开并编写一个基本的 class 文件格式解析器以获得完全限定的 class 名称。对于有经验的 java 程序员来说很容易。对于 java 的新手来说相当棘手(如果你认为这是个好主意,我猜你就是这样)。

您也可以使用现有工具来执行此操作,例如 bytebuddy 或 asm。

最后,您可以尝试一种面面俱到的方法:继续向上移动目录,直到它起作用。你知道,如果发生异常,它是行不通的。

例如,要加载 C:\MyDir\Whatever\com\foo\MyApp.class,您首先尝试创建一个新的 classloader(参见 URLClassLoader 的 API,它是核心 [=64] 的一部分=]) 作为根目录使用 C:\MyDir\Whatever\com\foo,然后你要求它加载 class MyApp.

如果可行,那就太好了(但通常尝试加载无包 classes 只是一个非启动器,你不应该这样做,CL API 可能不会支持一下,有意的,没有固定的)。

如果没有,请尝试 C:\MyDir\Whatever\com,然后加载 class foo.MyApp。如果这不起作用,请尝试 C:\MyDir\Whatever 并加载 class com.foo.MyApp,依此类推。

相当大的优势是,如果 MyApp.class 旁边还有另一个 class,并且 MyApp 需要它,这将工作正常。

您需要编写一个 while 循环(使用 Paths.getp.getParent() 遍历路径结构),捕获正确的异常,将路径操作为 class 名称(使用 .replace+),当然,创建一个 class 加载程序 (URLClassLoader),用它加载 classes(调用 loadClass),如果你打算 运行 宁它,像 thatClass.getConstructor().newInstance() 然后 thatClass.getMethod("someMethod", String.class, /* all the other args here */).invoke(theInstanceYouJustMade, "param1", /*all other params */) 实际上 'run' 它,更多可以在 java.lang.reflect 中找到包.