如何以编程方式编译和实例化Java类?

时间:2010-05-31 22:53:12

标签: java reflection dynamic-loading

我将类名存储在属性文件中。我知道类存储将实现IDynamicLoad。如何动态实例化该类?

现在我有

     Properties foo = new Properties();
    foo.load(new FileInputStream(new File("ClassName.properties")));
    String class_name = foo.getProperty("class","DefaultClass");
    //IDynamicLoad newClass = Class.forName(class_name).newInstance();

newInstance只加载编译的.class文件吗?如何加载未编译的Java类?

3 个答案:

答案 0 :(得分:130)

  

如何加载未编译的Java类?

您需要先编译它。这可以使用javax.tools API以编程方式完成。这只需要在JRE顶部的本地计算机上安装JDK

这是一个基本的启动示例(除了明显的异常处理):

// Prepare source somehow.
String source = "package test; public class Test { static { System.out.println(\"hello\"); } public Test() { System.out.println(\"world\"); } }";

// Save source in .java file.
File root = new File("/java"); // On Windows running on C:\, this is C:\java.
File sourceFile = new File(root, "test/Test.java");
sourceFile.getParentFile().mkdirs();
Files.write(sourceFile.toPath(), source.getBytes(StandardCharsets.UTF_8));

// Compile source file.
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
compiler.run(null, null, null, sourceFile.getPath());

// Load and instantiate compiled class.
URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { root.toURI().toURL() });
Class<?> cls = Class.forName("test.Test", true, classLoader); // Should print "hello".
Object instance = cls.newInstance(); // Should print "world".
System.out.println(instance); // Should print "test.Test@hashcode".

产生的结果如

hello
world
test.Test@ab853b

如果那些类implements已经在类路径中的某个接口,那么进一步使用会更容易。

SomeInterface instance = (SomeInterface) cls.newInstance();

否则,您需要让Reflection API访问并调用(未知)方法/字段。


这说的与实际问题无关:

properties.load(new FileInputStream(new File("ClassName.properties")));

java.io.File依赖当前工作目录是可移植性问题的秘诀。不要那样做。将该文件放在类路径中,并使用ClassLoader#getResourceAsStream()和类路径相对路径。

properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("ClassName.properties"));

答案 1 :(得分:6)

与BalusC的答案一脉相承,但在我的kilim发行版的这段代码中有更多的自动包装器。 https://github.com/kilim/kilim/blob/master/src/kilim/tools/Javac.java

它包含一个包含Java源代码的字符串列表,提取包和公共类/接口名称,并在tmp目录中创建相应的目录/文件层次结构。然后它在其上运行java编译器,并返回名称,类文件对(ClassInfo结构)的列表。

帮助自己完成代码。这是麻省理工学院的许可。

答案 2 :(得分:5)

如果您知道该类具有公共无参数构造函数,则您的注释代码是正确的。您只需要转换结果,因为编译器无法知道该类实际上将实现IDynamicLoad。所以:

   IDynamicLoad newClass = (IDynamicLoad) Class.forName(class_name).newInstance();

当然,必须编译类并在类路径上进行操作。

如果你想从源代码动态编译一个类,那就是另外一个鱼。