2010-05-31 92 views
59

我有存儲在屬性文件中的類名。我知道類存儲將實現IDynamicLoad。我如何動態地實例化類?如何以編程方式編譯和實例化Java類?

現在我有

 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類?

回答

118

如何加載未編譯的Java類?

您需要先編譯它。這可以用javax.tools API以編程方式完成。這隻需要在本地機器上安裝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 "[email protected]". 

其中產量喜歡

hello 
world 
[email protected] 

進一步使用會比較容易,如果這些類implements某個接口這已經是在類路徑中。

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

否則,你需要將Reflection API涉及訪問和調用(未知)方法/場。


也就是說和無關的實際問題:

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

java.io.File依靠當前工作目錄是便攜麻煩配方。不要這樣做。將該文件放入classpath中,並使用ClassLoader#getResourceAsStream()以及類路徑相對路徑。

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

只是一個脫離主題的問題。當我用你的方式加載屬性時,我得到一個空值,但當我做到這一點時,我得到的屬性Foo.class.getResourceAsStream()?你能幫我理解你的代碼嗎?謝謝。 – unj2 2010-06-01 15:44:15

+0

該屬性文件顯然與'Foo'類放在同一個包中。如上所述,你需要指定一個類路徑相對路徑,例如'COM /示例/ filename.properties'。但是如果你可以保證屬性文件總是和'Foo'類在同一個包中,那麼'Class#getResourceAsStream()'也是可以的。您只會錯過將應用程序外部的屬性文件外部化的功能,以便可以在不修改/重新打包應用程序的情況下對其進行修改。 – BalusC 2010-06-01 16:18:06

+0

'Files.write(source,sourceFile,StandardCharsets.UTF_8);'不爲我編譯。我想你想'Files.write(sourceFile.toPath(),source.getBytes());'。 – 2016-07-13 18:17:38

4

如果您知道該類具有公共無參數構造函數,那麼您的註釋代碼是正確的。你只需要輸出結果,因爲編譯器無法知道該類實際上會執行IDynamicLoad。所以:

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

當然,類必須編譯,並在類路徑中工作。

如果你正在尋找從源代碼動態編譯一個類,那是一個完整的其他水壺。

5

在同樣的BalusC的答案,但更有點自動包裝是在這裏這段代碼從我基裏姆分佈。 https://github.com/kilim/kilim/blob/master/src/kilim/tools/Javac.java

它需要一個包含Java源代碼的字符串列表,提取包和公共類/接口名稱,並在tmp目錄中創建相應的目錄/文件層次結構。然後運行它上面的java編譯器,並返回名稱,類文件對(ClassInfo結構)的列表。

幫助你自己的代碼。這是MIT許可的。

+0

如果編譯失敗會發生什麼? – 2016-07-13 17:44:16

相關問題