熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java核心技術 >> 正文

Java進階:SE6調用編譯器的兩種方法[1]

2013-11-23 19:39:05  來源: Java核心技術 

  在很多Java應用中需要在程序中調用Java編譯器來編譯和運行但在早期的版本中(Java SE及以前版本)中只能通過toolsjar中的comsuntoolsjavac包來調用Java編譯器但由於toolsjar不是標准的Java庫在使用時必須要設置這個jar的路徑而在Java SE中為我們提供了標准的包來操作Java編譯器這就是javaxtools包使用這個包我們可以不用將jar文件路徑添加到classpath中了

  一使用JavaCompiler接口來編譯Java源程序

  使用Java API來編譯Java源程序有很多方法現在讓我們來看一種最簡單的方法通過JavaCompiler進行編譯

  我們可以通過ToolProvider類的靜態方法getSystemJavaCompiler來得到一個JavaCompiler接口的實例

  JavaCompiler compiler = ToolProvidergetSystemJavaCompiler();

  JavaCompiler中最核心的方法是run通過這個方法可以編譯java源程序這個方法有個固定參數和個可變參數(可變參數是從Jave SE開始提供的一個新的參數類型用type… argu表示)個參數分別用來為java編譯器提供參數得到Java編譯器的輸出信息以及接收編譯器的錯誤信息後面的可變參數可以傳入一個或多個Java源程序文件如果run編譯成功返回

  int run(InputStream in OutputStream out OutputStream err String arguments)

  如果前個參數傳入的是null那麼run方法將以標准的輸入輸出代替即SysteminSystemout和Systemerr如果我們要編譯一個testjava文件並將使用標准輸入輸出run的使用方法如下

  int results = toolrun(null null null testjava);

  下面是使用JavaCompiler的完整代碼

  import javaio*;
  import javaxtools*;
  public class test_compilerapi
  {
   public static void main(String args[]) throws IOException
   {
    JavaCompiler compiler = ToolProvidergetSystemJavaCompiler();
    int results = compilerrun(null null null testjava);
    Systemoutprintln((results == )?編譯成功:編譯失敗);
    // 在程序中運行test
    Runtime run = RuntimegetRuntime();
    Process p = runexec(java test);
    BufferedInputStream in = new BufferedInputStream(pgetInputStream());
    BufferedReader br = new BufferedReader(new InputStreamReader(in));
    String s;
    while ((s = brreadLine()) != null)
    Systemoutprintln(s);
   }
  }
  public class test
  {
   public static void main(String[] args) throws Exception
   {
    Systemoutprintln(JavaCompiler測試成功!);
   }
  }

  編譯成功的輸出結果

  編譯成功

  JavaCompiler測試成功

  編譯失敗的輸出結果

  testjava:: 找不到符號

  符號 方法 printlnln(javalangString)

  位置 類 javaioPrintStream

  Systemoutprintlnln(JavaCompiler測試成功!);

  ^

   錯誤

  編譯失敗

  二使用StandardJavaFileManager編譯Java源程序

  在第一部分我們討論調用java編譯器的最容易的方法這種方法可以很好地工作但它確不能更有效地得到我們所需要的信息如標准的輸入輸出信息而在Java SE中最好的方法是使用StandardJavaFileManager類這個類可以很好地控制輸入輸出並且可以通過DiagnosticListener得到診斷信息而DiagnosticCollector類就是listener的實現

  使用StandardJavaFileManager需要兩步首先建立一個DiagnosticCollector實例以及通過JavaCompiler的getStandardFileManager()方法得到一個StandardFileManager對象最後通過CompilationTask中的call方法編譯源程序

  在使用這種方法調用Java編譯時最復雜的方法就是getTask下面讓我們討論一下getTask方法這個方法有如下所示的個參數

  getTask(Writer outJavaFileManager fileManager
  DiagnosticListener diagnosticListener
  Iterable options
  Iterable classes
  Iterable compilationUnits)

  這些參數大多數都可為null它們的含義所下

  ·out:用於輸出錯誤的流默認是Systemerr

  ·fileManager:標准的文件管理

  ·diagnosticListener: 編譯器的默認行為

  ·options: 編譯器的選項

  ·classes參與編譯的class

  最後一個參數compilationUnits不能為null因為這個對象保存了你想編譯的Java文件

  在使用完getTask後需要通過StandardJavaFileManager的getJavaFileObjectsFromFiles或getJavaFileObjectsFromStrings方法得到compilationUnits對象調用這兩個方法的方式如下

  Iterable getJavaFileObjectsFromFiles(
  Iterable files)
  Iterable getJavaFileObjectsFromStrings(
  Iterable names)
  String[] filenames = …;
  Iterable compilationUnits =
  fileManagergetJavaFileObjectsFromFiles(ArraysasList(filenames));
  JavaCompilerCompilationTask task = compilergetTask(null fileManager
  diagnostics options null compilationUnits);
  最後需要關閉fileManagerclose();

  下面是一個完整的演示程序

  import javaio*;
  import javautil*;
  import javaxtools*;
  public class test_compilerapi
  {
   private static void compilejava() throws Exception
   {
    JavaCompiler compiler = ToolProvidergetSystemJavaCompiler();
    // 建立DiagnosticCollector對象
    DiagnosticCollector diagnostics = new DiagnosticCollector();
    StandardJavaFileManager fileManager = compilergetStandardFileManager(diagnostics null null);
    // 建立用於保存被編譯文件名的對象
    // 每個文件被保存在一個從JavaFileObject繼承的類中
    Iterable compilationUnits = fileManager
    getJavaFileObjectsFromStrings(Arrays asList(testjava));
    JavaCompilerCompilationTask task = compilergetTask(null fileManager
    diagnostics null null compilationUnits);
    // 編譯源程序
    boolean success = taskcall();
    fileManagerclose();
    Systemoutprintln((success)?編譯成功:編譯失敗);
   }
   public static void main(String args[]) throws Exception
   {
    compilejava();
   }
  }

  如果想得到具體的編譯錯誤可以對Diagnostics進行掃描代碼如下

  for (Diagnostic diagnostic : diagnosticsgetDiagnostics())
  Systemoutprintf(
  Code: %s%n +
  Kind: %s%n +
  Position: %s%n +
  Start Position: %s%n +
  End Position: %s%n +
  Source: %s%n +
  Message: %s%n
  diagnosticgetCode() diagnosticgetKind()
  diagnosticgetPosition() diagnosticgetStartPosition()
  diagnosticgetEndPosition() diagnosticgetSource()
  diagnosticgetMessage(null));
  被編譯的testjava代碼如下
  public class test
  {
   public static void main(String[] args) throws Exception
   {
    aa; //錯誤語句
    Systemoutprintln(JavaCompiler測試成功!);
   }
  }

  在這段代碼中多寫了個aa得到的編譯錯誤為

  Code: compilererrnotstmt
  Kind: ERROR
  Position:
  Start Position:
  End Position:
  Source: testjava
  Message: testjava:: 不是語句
  Success: false

  通過JavaCompiler進行編譯都是在當前目錄下生成class文件而使用編譯選項可以改變這個默認目錄編譯選項是一個元素為String類型的Iterable集合如我們可以使用如下代碼在D盤根目錄下生成class文件

  Iterable options = ArraysasList(d d:\\);
  JavaCompilerCompilationTask task = compilergetTask(null fileManager
  diagnostics options null compilationUnits);

  在上面的例子中options處的參數為null而要傳遞編譯器的參數就需要將options傳入

  有時我們編譯一個Java源程序文件而這個源程序文件需要另幾個Java文件而這些Java文件又在另外一個目錄那麼這就需要為編譯器指定這些文件所在的目錄

  Iterable options = ArraysasList(sourcepath d:\\src);

  上面的代碼指定的被編譯Java文件所依賴的源文件所在的目錄

[]  []  


From:http://tw.wingwit.com/Article/program/Java/hx/201311/27239.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.