2013-02-07 19 views
3

我已經創建了加載用戶選擇的java(JPanel)文件的程序。用戶基本上選擇由JavaCompiler編譯並加載下一個生成的類文件的Java文件。 但是,當通過某些文本編輯器在java文件(JPanel)中完成任何更改時,問題即將到來,因爲即使在關閉程序並重新運行項目後,任何新更改都不會反映在類文件中。清除通過java中的class.forName加載的類

我認爲相同的類文件一次又一次地從內存中加載。

有沒有什麼辦法從內存中清除加載的類?

編譯:

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 
if (compiler != null) { 
     DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>(); 
     StandardJavaFileManager stdFileManager = compiler.getStandardFileManager(diagnostics, null, null); 
     Iterable<? extends JavaFileObject> fileObjects = stdFileManager.getJavaFileObjectsFromFiles(filesToCompile); 
     List<String> optionList = new ArrayList<String>(); 
     // set compiler's classpath to be same as the runtime's 
     rootDir=Utility.createRootDir(); 
     optionList.addAll(Arrays.asList("-d", rootDir.getAbsolutePath(), "-classpath", System.getProperty("java.class.path"))); 
     // optionList.add(() 
     try { 
      stdFileManager.flush(); 
     } catch (IOException e1) { 
      e1.printStackTrace(); 
     } 
     CompilationTask task = compiler.getTask(null, stdFileManager,null, optionList, null, fileObjects); 
     Boolean result = task.call(); 
     try { 
      stdFileManager.flush(); 
      stdFileManager.close(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
} 

加載:

loader = new URLClassLoader(new URL[] { rootDir.toURI().toURL() }); 
cls = Class.forName(Utility.extractFQDN(sourceFile)+"."+Utility.extractClassName(sourceFile),true, loader); 

panel= (JPanel) cls.newInstance();

我已經檢查了編譯後的class文件反編譯器與它更新的代碼,但我不知道爲什麼以前的類文件是按類從內存中加載的裝載機。

+0

檢查task.call()後的結果 - 是否有任何錯誤? –

+0

成功生成類文件沒有錯誤。 – WitVault

回答

1

編輯:

下面是一個SSCCE編譯字符串重複相同的類名,並展示新的行爲。爲了避免整個文件的混亂,它會在內存中執行所有操作。我認爲這應該很容易適應您的應用程序。

import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.io.OutputStream; 
import java.lang.reflect.Method; 
import java.net.URI; 
import java.util.Collections; 
import java.util.HashMap; 
import java.util.Map; 

import javax.tools.FileObject; 
import javax.tools.ForwardingJavaFileManager; 
import javax.tools.JavaCompiler; 
import javax.tools.JavaFileManager; 
import javax.tools.JavaFileObject; 
import javax.tools.JavaFileObject.Kind; 
import javax.tools.SimpleJavaFileObject; 
import javax.tools.ToolProvider; 


public class Compile { 
    static class OutFile extends SimpleJavaFileObject { 
     private final ByteArrayOutputStream out = new ByteArrayOutputStream(); 

     OutFile(String name) { 
      super(URI.create("memory:///" + name.replace('.','/') + Kind.CLASS.extension), Kind.CLASS); 
     } 

     @Override 
     public OutputStream openOutputStream() throws IOException { 
      return out; 
     } 
    } 

    static class InFile extends SimpleJavaFileObject { 
     final String code; 

     InFile(String name, String code) { 
      super(URI.create("string:///" + name.replace('.','/') + Kind.SOURCE.extension), Kind.SOURCE); 
      this.code = code; 
     } 

     @Override 
     public CharSequence getCharContent(boolean ignoreEncodingErrors) { 
      return code; 
     } 
    } 

    static class Loader extends ClassLoader { 
     private final Map<String, OutFile> files = new HashMap<String, OutFile>(); 

     public OutFile add(String className) { 
      OutFile file = new OutFile(className); 
      files.put(className, file); 
      return file; 
     } 

     @Override 
     protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { 
      Class<?> c = findLoadedClass(name); 
      if(c == null) { 
       OutFile file = files.get(name); 
       if(file == null) { 
        return super.loadClass(name, resolve); 
       } 

       c = defineClass(name, file.out.toByteArray(), 0, file.out.size()); 
      } 

      if(resolve) { 
       resolveClass(c); 
      } 

      return c; 
     } 
    } 

    static class FileManager extends ForwardingJavaFileManager<JavaFileManager> { 
     private final Loader loader = new Loader(); 

     protected FileManager(JavaFileManager fileManager) { 
      super(fileManager); 
     } 

     public Class<?> getClass(String name) throws ClassNotFoundException { 
      return loader.loadClass(name); 
     } 

     @Override 
     public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException { 
      return loader.add(className); 
     } 
    } 

    public static void compileAndRun(String source) throws Exception { 
     InFile in = new InFile("Main", "class Main {\npublic static void main(String[] args) {\n" + source + "\n}\n}"); 

     JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 
     FileManager manager = new FileManager(compiler.getStandardFileManager(null, null, null)); 

     compiler.getTask(null, manager, null, null, null, Collections.singletonList(in)).call(); 

     Method method = manager.getClass("Main").getMethod("main", String[].class); 
     method.setAccessible(true); 
     method.invoke(null, (Object)new String[0]); 
    } 

    public static void main(String[] args) throws Exception { 
     compileAndRun("System.out.println(\"Hello\");"); 
     compileAndRun("System.out.println(\"World\");"); 
    } 
} 

原文:

ClassLoader(和子類像URLClassLoader)總是問父類加載器加載類,如果有一個父。如果在構建父項時沒有明確設置父項,則父項設置爲系統類加載器。所以,你創建的所有新類加載器都推遲到已經定義類的系統類加載器。

爲了得到你想要的行爲,家長設置爲null:

loader = new URLClassLoader(new URL[] { rootDir.toURI().toURL() }, null); 

編輯:請注意,這只是一個問題,因爲你正在編譯你的類到根目錄下,這也是對系統類加載器的類路徑。如果您編譯到某個臨時目錄,系統類加載器將無法找到該類,並且URL加載器會自動加載該類。

+0

現在我得到java.lang.ClassNotFoundException – WitVault

+0

對於你正在加載的類,或另一個類?現在我發現你可能需要給URLClassLoader提供系統類路徑,所以它可以加載任何相關的類。這個,或者是ClassLoader的子類來加載你自己編譯的類,但是對於所有其他的類都要遵循系統類加載器。 –

+0

對於我沒有任何其他課程的課程。 – WitVault