我将类名存储在属性文件中。我知道类存储将实现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类?
答案 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();
当然,必须编译类并在类路径上进行操作。
如果你想从源代码动态编译一个类,那就是另外一个鱼。