2

情况:

我正在制作一个用于绘制算法时间复杂度的模拟器。学生可以添加加载自己的 .java 文件来运行它。我的程序编译(使用'JavaCompiler').java 文件。然后它会尝试加载 .class 文件:

loadedClass = loadClass("customAlgorithms."+this.getClassName()+"");

在 Eclipse 中运行我的程序时,一切正常,学生可以完美地使用该程序。

问题:

但后来我将我的项目导出到一个可执行的 jar 文件。编译部分仍然有效,但加载类失败,因为它在 jar 文件中搜索

我想知道为什么我不能这样做:(更改 . 与 /)

loadedClass = loadClass("customAlgorithms/"+this.getClassName()+"");

有哪些可能的选择?这是我能想到的:

  • 将编译后的 .class 文件添加到当前运行的 .jar 文件中。这甚至可以工作吗?

    我知道可以编辑 jar 档案。但是这可以在运行时工作而无需重新启动程序吗?

  • 其他使用“loadClass()”的方法?这样我就可以加载一个不包含在我的 jar 文件中的类文件

还有其他想法吗?

4

3 回答 3

3

Java 通过 ClassLoaders 加载类。当您启动 JVM 时,您必须告诉 JVM 它的“类路径”,即类所在的文件夹或 jar 文件的列表。JVM 将创建一个 ClassLoader(通常是 URLClassLoader 的子类)并给这个 ClassLoader 文件夹和 jar 文件的列表,以便类加载器能够加载该类。

在您的应用程序中,当您编译 java 源文件时,它会生成一个类文件,该文件保存在某处。大概在eclipse中,保存class文件的文件夹就是classpath中包含的文件夹,这样JVM就能找到class。

当您将项目导出到正在运行的 jar 文件时,JVM 可能会在其类路径中仅包含该 JAR 文件启动,因此它不会在其他任何地方搜索您编译的类。

您有几种方法可以解决此问题。按复杂度递增的顺序:

  1. 启动您的 jar 不是作为可执行 jar,而是使用 .bat/.sh 或任何其他可以配置类路径的 java 运行程序,将特定文件夹添加到类路径,将 java 源文件编译到该文件夹​​。
  2. 编译 java 文件时,创建一个文件夹并创建一个指向该文件夹的 URLClassLoader 的新实例。将 java 文件编译到该文件夹​​。然后使用该 URLCLassLoader 加载编译后的类。
  3. 将 java 源代码编译到 RAM,例如字节数组,并实现一个自定义类加载器,该类加载器将返回该字节数组,而不是从磁盘加载。

解决方案 1 非常简单,但也有其缺点:如果不再次运行整个程序,将无法重新加载编译后的 java 文件(一旦在类加载器中加载了一个类,就无法再次加载,除非使用代理在这个问题的范围之外),它需要程序写入磁盘,这意味着权限等。

解决方案 2 及其仅是 RAM 的演变要优雅得多,但要复杂得多:从与整个应用程序其余部分使用的类加载器不同的类加载器加载单个类是棘手的,您可能会得到奇怪的类异常(如 ClassCastExceptions , ClassNotFoundException 等)由于类加载器之间的不匹配。

于 2011-08-31T10:55:46.363 回答
1

您可以使用ClassLoader defineClass 方法。它受到保护,但应该没有问题。

如果方法受保护的事实是一个问题,可能在 Internet 的某个地方,您可以定义一个自定义子类,该子类具有免费提供的功能。

于 2011-08-31T10:49:25.927 回答
1

您必须创建另一个类加载器,例如 URLClassLoader 并从中加载类。

于 2011-08-31T10:50:14.117 回答