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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2946338/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 14:48:38  来源:igfitidea点击:

How do I programmatically compile and instantiate a Java class?

javareflectiondynamic-loading

提问by unj2

I have the class name stored in a property file. I know that the classes store will implement IDynamicLoad. How do I instantiate the class dynamically?

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

Right now I have

现在我有

     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();

Does the newInstance only load compiled .class files? How do I load a Java Class that is not compiled?

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

采纳答案by BalusC

How do I load a Java Class that is not compiled?

如何加载未编译的 Java 类?

You need to compile it first. This can be done programmatically with the javax.toolsAPI. This only requires the JDKbeing installed at the local machine on top of JRE.

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

Here's a basic kickoff example (leaving obvious exception handling aside):

这是一个基本的启动示例(将明显的异常处理放在一边):

// 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".

Which yields like

哪个产生像

hello
world
test.Test@ab853b

Further use would be more easy if those classes implementsa certain interface which is already in the classpath.

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

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

Otherwise you need to involve the Reflection APIto access and invoke the (unknown) methods/fields.

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



That said and unrelated to the actual problem:

也就是说,与实际问题无关:

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

Letting java.io.Filerely on current working directory is recipe for portability trouble. Don't do that. Put that file in classpath and use ClassLoader#getResourceAsStream()with a classpath-relative path.

java.io.File依靠当前工作目录是便携麻烦配方。不要那样做。将该文件放在类路径中并ClassLoader#getResourceAsStream()与类路径相对路径一起使用。

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

回答by Yishai

Your commented code is correct if you know that the class has a public no-arg constructor. You just have to cast the result, as the compiler can't know that the class will in fact implement IDynamicLoad. So:

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

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

Of course the class has to be compiled and on the classpath for that to work.

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

If you are looking to dynamically compile a class from source code, that is a whole other kettle of fish.

如果您希望从源代码动态编译一个类,那完全是另一回事。

回答by Sriram Srinivasan

In the same vein as BalusC's answer, but a bit more automatic wrapper is here in this piece of code from my kilim distribution. https://github.com/kilim/kilim/blob/master/src/kilim/tools/Javac.java

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

It takes a list of strings containing Java source, extracts the package and public class/interface names and creates the corresponding directory/file hierarchy in a tmp directory. It then runs the java compiler on it, and returns a list of name,classfile pairs (the ClassInfo structure).

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

Help yourself to the code. It is MIT licensed.

帮助自己编写代码。它是 MIT 许可的。