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

unj*_*nj2 64 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();
Run Code Online (Sandbox Code Playgroud)

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

Bal*_*usC 133

如何加载未编译的Java类?

你需要先编译它.这可以通过javax.toolsAPI以编程方式完成.这只需要将JDK安装在JRE之上的本地计算机上.

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

// 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".
Run Code Online (Sandbox Code Playgroud)

产量如何

hello
world
test.Test@ab853b
Run Code Online (Sandbox Code Playgroud)

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

SomeInterface instance = (SomeInterface) cls.newInstance();
Run Code Online (Sandbox Code Playgroud)

否则,您需要使用Reflection API来访问和调用(未知)方法/字段.


这说和实际问题无关:

properties.load(new FileInputStream(new File("ClassName.properties")));
Run Code Online (Sandbox Code Playgroud)

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

properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("ClassName.properties"));
Run Code Online (Sandbox Code Playgroud)

  • 是否可以在内存中编译类,将生成的字节码作为结果字节数组并将其存储在其他地方(例如在网络上或数据库中)以便自定义类加载器可以检索它?(而不是写入本地文件)。 (2认同)

Sri*_*san 6

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

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

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


Yis*_*hai 5

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

   IDynamicLoad newClass = (IDynamicLoad) Class.forName(class_name).newInstance();
Run Code Online (Sandbox Code Playgroud)

当然,必须编译类并在类路径上才能工作.

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