<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆 - 312, 文章 - 14, 評論 - 1393, 引用 - 0
    數據加載中……

    Java SE6調用Java編譯器的兩種新方法

    本文為原創,如需轉載,請注明作者和出處,謝謝!

    本文曾發表于天極網:http://dev.yesky.com/451/3039451.shtml

    在很多Java應用中需要在程序中調用Java編譯器來編譯和運行。但在早期的版本中(Java SE5及以前版本)中只能通過tools.jar中的com.sun.tools.javac包來調用Java編譯器,但由于tools.jar不是標準的Java庫,在使用時必須要設置這個jar的路徑。而在Java SE6中為我們提供了標準的包來操作Java編譯器,這就是javax.tools包。使用這個包,我們可以不用將jar文件路徑添加到classpath中了。

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

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

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

    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

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

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

    如果前3個參數傳入的是null,那么run方法將以標準的輸入、輸出代替,即System.inSystem.outSystem.err。如果我們要編譯一個test.java文件,并將使用標準輸入輸出,run的使用方法如下:

    int results = tool.run(null, null, null, "test.java");

    下面是使用JavaCompiler的完整代碼。


    import java.io.*;
    import javax.tools.*;

    public class test_compilerapi
    {
        
    public static void main(String args[]) throws IOException
        {
            JavaCompiler compiler 
    = ToolProvider.getSystemJavaCompiler();
            
    int results = compiler.run(nullnullnull"test.java");
            System.out.println((results 
    == 0)?"編譯成功":"編譯失敗");
    // 在程序中運行test
            Runtime run = Runtime.getRuntime();
            Process p 
    = run.exec("java test");
            BufferedInputStream in 
    = new BufferedInputStream(p.getInputStream());
            BufferedReader br 
    = new BufferedReader(new InputStreamReader(in));
            String s;
            
    while ((s = br.readLine()) != null)
                System.out.println(s);
        }
    }    


    public class test
    {
        
    public static void main(String[] args) throws Exception
        {
            System.out.println(
    "JavaCompiler測試成功!");
        }
    }

    編譯成功的輸出結果:

    編譯成功

    JavaCompiler測試成功

    編譯失敗的輸出結果:

    test.java:9: 找不到符號

    符號:方法 printlnln(java.lang.String)

    位置: java.io.PrintStream

                  System.out.printlnln("JavaCompiler測試成功!");

                            ^

    1 錯誤

    編譯失敗

    二、使用StandardJavaFileManager編譯Java源程序

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

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

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


    getTask(Writer out,

    JavaFileManager fileManager,

    DiagnosticListener
    <? super JavaFileObject> diagnosticListener,

    Iterable
    <String> options,

    Iterable
    <String> classes,

    Iterable
    <? extends JavaFileObject> compilationUnits)

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

    1.         out::用于輸出錯誤的流,默認是System.err。

    2.         fileManager::標準的文件管理。

    3.         diagnosticListener: 編譯器的默認行為。

    4.         options: 編譯器的選項

    5.         classes:參與編譯的class

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

    在使用完getTask后,需要通過StandardJavaFileManagergetJavaFileObjectsFromFilesgetJavaFileObjectsFromStrings方法得到compilationUnits對象。調用這兩個方法的方式如下:.


    Iterable<? extends JavaFileObject> getJavaFileObjectsFromFiles(
    Iterable
    <? extends File> files)
    Iterable
    <? extends JavaFileObject> getJavaFileObjectsFromStrings(
    Iterable
    <String> names)

    String[] filenames 
    = …;
    Iterable
    <? extends JavaFileObject> compilationUnits =
    fileManager.getJavaFileObjectsFromFiles(Arrays.asList(filenames));

        JavaCompiler.CompilationTask task 
    = compiler.getTask(null, fileManager,
                            diagnostics, options, 
    null, compilationUnits);

        最后需要關閉fileManager.close();

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


    import java.io.*;
    import java.util.*;
    import javax.tools.*;

    public class test_compilerapi
    {
        
    private static void compilejava() throws Exception
        {
            JavaCompiler compiler 
    = ToolProvider.getSystemJavaCompiler();
            
    // 建立DiagnosticCollector對象
            DiagnosticCollector<JavaFileObject> diagnostics 
    = new DiagnosticCollector<JavaFileObject>();      
            StandardJavaFileManager fileManager 
    = compiler.getStandardFileManager(
                            diagnostics, 
    nullnull);
            
    // 建立用于保存被編譯文件名的對象
            
    // 每個文件被保存在一個從JavaFileObject繼承的類中      
            Iterable<? extends JavaFileObject> compilationUnits = fileManager
                            .getJavaFileObjectsFromStrings(Arrays asList(
    "test3.java"));
            JavaCompiler.CompilationTask task 
    = compiler.getTask(null, fileManager,
                            diagnostics, 
    nullnull, compilationUnits);
            
    // 編譯源程序
            boolean success = task.call();
            fileManager.close();
            System.out.println((success)
    ?”編譯成功”:”編譯失敗”);
        }
        
    public static void main(String args[]) throws Exception
        {
            compilejava();
        }
    }

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


            for (Diagnostic diagnostic : diagnostics.getDiagnostics())
                System.out.printf(
                
    "Code: %s%n" +
                
    "Kind: %s%n" +
                
    "Position: %s%n" +
                
    "Start Position: %s%n" +
                
    "End Position: %s%n" +
                
    "Source: %s%n" +
                
    "Message: %s%n",
                diagnostic.getCode(), diagnostic.getKind(),
                diagnostic.getPosition(), diagnostic.getStartPosition(),
                diagnostic.getEndPosition(), diagnostic.getSource(),
                diagnostic.getMessage(
    null));

    被編譯的test.java代碼如下:

    public class test
    {
        
    public static void main(String[] args) throws Exception
        {
            aa;  
    //錯誤語句
            System.out.println("JavaCompiler測試成功!");
        }
    }


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

    Code: compiler.err.not.stmt

    Kind: ERROR

    Position: 89

    Start Position: 89

    End Position: 89

    Source: test.java

    Message: test.java:5: 不是語句

    Success: false

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


    Iterable<String> options = Arrays.asList("-d""d:\\");
    JavaCompiler.CompilationTask task 
    = compiler.getTask(null, fileManager,
                        diagnostics, options, 
    null, compilationUnits);

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

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

    Iterable<String> options = Arrays.asList("-sourcepath", "d:""src");

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

     

    三、在內存中編譯

    JavaCompiler不僅可以編譯硬盤上的Java文件,而且還可以編譯內存中的Java代碼,然后使用reflection來運行它們。我們可以編寫一個JavaSourceFromString類,通過這個類可以輸入Java源代碼。一但建立這個對象,你可以向其中輸入任意的Java代碼,然后編譯和運行,而且無需向硬盤上寫.class文件。

    import java.lang.reflect.*;
    import java.io.*;
    import javax.tools.*;
    import javax.tools.JavaCompiler.CompilationTask;
    import java.util.*;
    import java.net.*;

    public class test_compilerapi
    {
        
    private static void compilerJava() throws Exception
        {
            JavaCompiler compiler 
    = ToolProvider.getSystemJavaCompiler();
            DiagnosticCollector
    <JavaFileObject> diagnostics 
    = new DiagnosticCollector<JavaFileObject>();
            
    // 定義一個StringWriter類,用于寫Java程序
            StringWriter writer = new StringWriter();
            PrintWriter out 
    = new PrintWriter(writer);
            
    // 開始寫Java程序
            out.println("public class HelloWorld {");
            out.println(
    " public static void main(String args[]) {");
            out.println(
    " System.out.println(\"Hello, World\");");
            out.println(
    " }");
            out.println(
    "}");
            out.close();
            
    //為這段代碼取個名子:HelloWorld,以便以后使用reflection調用
            JavaFileObject file = new JavaSourceFromString("HelloWorld", writer
                            .toString());
            Iterable
    <? extends JavaFileObject> compilationUnits = Arrays
                            .asList(file);
            JavaCompiler.CompilationTask task 
    = compiler.getTask(nullnull,
                            diagnostics, 
    nullnull, compilationUnits);
            
    boolean success = task.call();
            System.out.println(
    "Success: " + success);
            
    // 如果成功,通過reflection執行這段Java程序
            if (success)
            {
                System.out.println(
    "-----輸出-----");
                Class.forName(
    "HelloWorld").getDeclaredMethod("main"new Class[]
                { String[].
    class }).invoke(nullnew Object[]
                { 
    null });
                System.out.println(
    "-----輸出 -----");
            }        
        }
        
    public static void main(String args[]) throws Exception
        {
            compilerJava();
        }
    }
    // 用于傳遞源程序的JavaSourceFromString類
    class JavaSourceFromString extends SimpleJavaFileObject
    {
        
    final String code;
        JavaSourceFromString(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;
        }
    }






    Android開發完全講義(第2版)(本書版權已輸出到臺灣)

    http://product.dangdang.com/product.aspx?product_id=22741502



    Android高薪之路:Android程序員面試寶典 http://book.360buy.com/10970314.html


    新浪微博:http://t.sina.com.cn/androidguy   昵稱:李寧_Lining

    posted on 2008-05-13 10:25 銀河使者 閱讀(3038) 評論(3)  編輯  收藏 所屬分類: java 原創

    評論

    # re: Java SE6調用Java編譯器的兩種新方法  回復  更多評論   

    沒東西發就別發 發了一年了多了 現在又拿出來重發 搞個毛
    2008-05-13 16:59 | as

    # re: Java SE6調用Java編譯器的兩種新方法[未登錄]  回復  更多評論   

    不同意樓上的觀點,每時每刻大家都在學習中。你學到不代表別人學到了。。呵呵??!話語如有過激,請多原諒
    2008-05-13 19:10 | Samuel

    # re: Java SE6調用Java編譯器的兩種新方法  回復  更多評論   

    最近只是將我在其他媒體發表過的或以前寫過的文章都放到blogjava和我在博客園的blog:nokiaguy.cnblogs.com上。做一個備份和總結,也希望對想學習相關知識的朋友有所幫助。 如果大家有什么意見,盡管提。哈哈?。。?
    2008-05-13 19:13 | 銀河使者
    主站蜘蛛池模板: 亚洲精品无码不卡| 亚洲一区二区中文| 亚洲人成电影网站国产精品 | 国产精品亚洲综合久久| 日韩成人精品日本亚洲| 老司机69精品成免费视频| 国产免费人成在线视频| 亚洲欧洲日产国码在线观看| 一二三四在线观看免费中文在线观看| 未满十八18禁止免费无码网站| 国产美女a做受大片免费| 亚洲午夜久久影院| 亚洲精品视频在线免费| 亚洲免费在线观看| 两个人看的www免费高清| 免费精品国产自产拍观看| 黄网站在线播放视频免费观看| 免费三级毛片电影片| 亚洲人成在线电影| 亚洲免费视频播放| 久久久久亚洲精品成人网小说| 免费大片av手机看片| 日韩a级毛片免费观看| 亚洲国产人成在线观看| 久久久久久夜精品精品免费啦| 天天摸天天操免费播放小视频| 久久亚洲春色中文字幕久久久| 在线视频观看免费视频18| 久久精品国产亚洲AV大全| 国拍在线精品视频免费观看| 99亚洲乱人伦aⅴ精品| 亚洲精品制服丝袜四区| 一级做a爰黑人又硬又粗免费看51社区国产精品视 | 国产精品极品美女自在线观看免费| 成人黄18免费视频| 亚洲短视频在线观看| 99热这里只有精品免费播放| 国产成人精品日本亚洲专一区| 国产高清免费视频| 91亚洲视频在线观看| 免费a级毛片网站|