編譯 Java 程式碼

JDT 外掛程式包含一個增量和批次的 Java 編譯器,可從程式碼建置 Java .class 檔案。 這個編譯器未提供直接的 API。它會安裝成 Java 專案中的一個建置器。編譯是以標準的平台建置機制來觸發。

有關平台建置機制的詳述,請參閱增量專案建置器

編譯器碼

您可以程式設計的方式使用建置 API 來編譯專案中的 Java 程式檔。

   IProject myProject;
IProgressMonitor myProgressMonitor;myProject.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, myProgressMonitor);

對於 Java 專案,這會呼叫 Java 增量專案建置器(以及任何已新增至專案之建置 規格內的其他增量專案建置器)。所產生的 .class 檔會寫到指定的輸出資料夾中。 其他資源檔案可複製到輸出資料夾中。 

如果建置了完整批次,則可以清除輸出資料夾中的所有 .class 檔,以確保沒有過時的檔案。 這是藉由使用「JDT 核心建置器選項」 (CORE_JAVA_BUILD_CLEAN_OUTPUT_FOLDER) 來控制的。這個選項的預設值是清除輸出資料夾。除非重設這個選項, 不然您必須確定您是將所有 .class 檔(這些檔案沒有對應的程式檔), 放在類別路徑中的個別類別檔資料夾中,而非放在輸出資料夾中。

您可以使用其他控制哪些資源將複製到輸出資料夾的選項, 來配置增量和批次建置器。下列範例顯示如何設定資源過濾器, 以便以 '.ignore' 結尾的檔案和名為 'META-INF' 的資料夾不會被複製到輸出資料夾:

      Hashtable options = JavaCore.getOptions();
   options.put(JavaCore.CORE_JAVA_BUILD_RESOURCE_COPY_FILTER, "*.ignore,META-INF/");
   JavaCore.setOptions(options);

當檔案名稱符合所提供的型樣之一,便會過濾檔案名稱。 如果資料夾的名稱符合所提供的資料夾名稱(尾端是路徑分隔字元)之一,便會過濾整個資料夾。

您也可以將增量和批次式建置器配置成當 .classpath 檔中有錯誤時只產生一個錯誤。 依預設會設定這個選項,而且它會消除大部分的錯誤。 請參閱 JDT 核心建置器選項,以取得建置器相關選項及其預設值的完整清單。

您也可以使用 JavaCore 選項,來配置編譯器。例如,您可以定義應該對不同種類的問題(在編譯期間找到的問題)使用的嚴重性。 請參閱 JDT 核心編譯器選項,以取得編譯器相關選項及其預設值的完整清單。

當以程式化的方式來配置建置器或編譯器的選項時,您應該指定選項的範圍。 例如,設定資源過濾器可能只適用於特定專案。

   
   Hashtable options = myProject.getOptions(false);  // get only the options set up in this project
   options.put(JavaCore.CORE_JAVA_BUILD_RESOURCE_COPY_FILTER, "*.ignore,META-INF/");
   myProject.setOptions(options);

使用批次編譯器

尋找批次編譯器

批次編譯器類別在 JDT 核心外掛程式的內部類別中。 該類別的名稱為 org.eclipse.jdt.internal.compiler.batch.Main。 它套裝在 plugins/org.eclipse.jdt.core_3.2.0.jar 中。 從 3.2 開始,它也可以個別下載。 檔案名稱是 ecj.jar。 對應的來源也能夠使用。 如果要取得它們,請移至下載頁面,搜尋 JDT 核心批次編譯器一節。 這個 JAR 包含批次編譯器和 javac Ant 配接器。

因此,您可以利用它來作為獨立式應用程式,也可以在 Eclipse 之外,在 Ant 建置之內使用它。

執行批次編譯器

有哪些可用的選項?

背景為橙色的選項是建議選項。

名稱 用法
類別路徑選項
-bootclasspath <dir 1>;<dir 2>;...;<dir P> 這是用來引導編譯器所用之類別檔的目錄或 JAR 檔清單。 依預設會使用執行中之 VM 的程式庫。這些項目會以平台路徑分隔字元來區隔。
每一個目錄或檔案可以在 '[' 和 ']' 之間指定類型的存取規則。
-cp
-classpath <dir 1>;<dir 2>;...;<dir P>
這是用來編譯程式檔的目錄或 JAR 檔清單。 預設值是內容 "java.class.path" 的值。這些項目會以平台路徑分隔字元來區隔。
每一個目錄或檔案可以在 '[' 和 ']' 之間指定類型的存取規則(例如,[-X] 表示禁止存取類型 X、[~X] 表示不建議存取類型 X,[+p/X:-p/*] 表示禁止存取套件 p 中的所有類型,但容許存取 p/X)。
-extdirs <dir 1>;<dir 2>;...;<dir P> 這是用來指定延伸規格 zip/jar 檔之位置的目錄清單。 這些項目會以平台路徑分隔字元來區隔。
-endorseddirs <dir 1>;<dir 2>;...;<dir P> 這是用來指定已認可的 zip/jar 檔之位置的目錄清單。 這些項目會以平台路徑分隔字元來區隔。
-sourcepath <dir 1>;<dir 2>;...;<dir P> 這是用來指定程式檔的目錄清單。 這些項目會以平台路徑分隔字元來區隔。
每一個目錄可以在 '[' 和 ']' 之間指定類型的存取規則。
-d <dir 1>|none 這是用來指定應在其中傾出產生之 .class 檔的目錄。如果省略,則不會建立任何套件目錄結構。
如果您不要產生任何 .class 檔,請使用 -d none
-encoding <encoding name> 請指定預設來源編碼格式(您也可以在每個輸入程式檔/資料夾名稱上附加 [<encoding name>],如 X.java[utf8],以在個別檔案的基礎上指定自訂編碼)。
標準選項
-target 1.1|1.2|1.3|1.4|1.5|5|5.0|1.6|6|6.0 這會指定 .class 檔目標設定。 可能的值為:
  • 1.1(主要版本:45 次要:3)
  • 1.2(主要版本:46 次要:0)
  • 1.3(主要版本:47 次要:0)
  • 1.4(主要版本:48 次要:0)
  • 1.555.0(主要版本:49 次要:0)
  • 1.666.0(主要版本:50 次要:0)
預設值為:
  • 1.1-1.3 模式)
  • 1.2-1.4 模式)
  • 1.5-1.5 模式)
  • 1.6-1.6 模式)
-1.3 將標準層次設為 1.3。隱含表示 -source 1.3 -target 1.1。
-1.4 將標準層次設為 1.4(預設值)。隱含表示 -source 1.3 -target 1.2。
-1.5 將標準層次設為 1.5。隱含表示 -source 1.5 -target 1.5。
-1.6 將標準層次設為 1.6。隱含表示 -source 1.6 -target 1.6。
-source 1.3|1.4|1.5|5|5.0|1.6|6|6.0 這用來指定編譯器所預期的來源層次。
可能的值為:
  • 1.3
  • 1.4
  • 1.555.0
  • 1.666.0
預設值為:
  • 1.3-1.3 模式)
  • 1.3-1.4 模式)
  • 1.5-1.5 模式)
  • 1.6-1.6 模式)
1.4 中,assert 會視為關鍵字。在 1.51.6 中,enumassert 都是當作關鍵字來處理。
警告選項
-warn:
allDeprecation
allJavadoc
assertIdentifier
boxing
charConcat
conditionAssign
constructorName
dep-ann
deprecation
discouraged
emptyBlock
enumSwitch
fallthrough
fieldHiding
finalBound
finally
forbidden
hiding
incomplete-switch
indirectStatic
intfAnnotation
intfNonInherited
javadoc
localHiding
maskedCatchBlocks
nls
noEffectAssign
null
over-ann
paramAssign
pkgDefaultMethod
raw
semicolon
serial
specialParamHiding
static-access
staticReceiver
suppress
synthetic-access
syntheticAccess
tasks(<task1>|...|<taskN>)
typeHiding
unchecked
unnecessaryElse
unqualified-field-access
unqualifiedField
unused
unusedArgument
unusedImport
unusedLabel
unusedLocal
unusedPrivate
unusedThrown
uselessTypeCheck
varargsCast
warningToken
設定警告層次。
例如:-warn:unusedLocal,deprecation

紅色項目為預設值。

    -warn:none                               停用所有警告
    -warn:<以 , 區隔的警告>    確實啟用列出的警告
    -warn:+<以 , 區隔的警告>   啟用其他的警告
    -warn:-<以 , 區隔的警告>   停用特定的警告
allDeprecation 棄用,即使在已棄用的程式碼內亦同
allJavadoc 無效或遺漏的 javadoc
assertIdentifier 用來作為 ID 的 assert
boxing autoboxing 轉換
charConcat 字元陣列用於字串連接,而不需明確轉換成字串的時機
conditionAssign 可能的意外 Boolean 指派
constructorName 含有建構子名稱的方法
dep-ann 遺漏 @Deprecated 註解
deprecation 在已棄用的程式碼之外,已棄用之類型或成員的用法
discouraged 使用的類型符合不建議的存取規則
emptyBlock 未記載的空區塊
enumSwitch,
incomplete-switch
不完整的 enum 切換選項
fallthrough 可能的 fall-through 案例
fieldHiding 欄位隱藏了另一個變數
finalBound 含有最終界限的類型參數
finally finally block 未正常完成
forbidden 使用的類型符合禁止存取規則
hiding fieldHiding、localHiding、typeHiding 和 maskedCatchBlock 的巨集
indirectStatic 靜態成員的間接參照
intfAnnotation 用來作為超介面的註解類型
intfNonInherited 介面非繼承方法的相容性
javadoc 無效的 javadoc
localHiding 區域變數隱藏了另一個變數
maskedCatchBlocks 隱藏的 catch 區塊
nls 非 nls 字串文字(缺少 //$NON-NLS-<n> 標示)
noEffectAssign 沒有作用的指派
null 遺漏或冗餘的空值檢查
over-ann 遺漏 @Override 註解
paramAssign 指派給參數
pkgDefaultMethod 嘗試置換套件預設方法
raw 使用原始類型(而不是參數化類型)
semicolon 不必要的分號或空的陳述式
serial 遺漏 serialVersionUID
specialParamHiding 建構子或 setter 參數隱藏了另一個欄位
static-access indirectStatic 和 staticReceiver 的巨集
staticReceiver 非 static 接收端是否用來取得 static 欄位或呼叫 static 方法
suppress 啟用 @SuppressWarnings
syntheticAccess,
synthetic-access
對內部類別執行綜合存取的時機
tasks 在程式碼中啟用 tasks 標示的支援
typeHiding 類型參數隱藏了另一個類型
unchecked 未檢查的 type 作業
unnecessaryElse 不必要的 else 子句
unqualified-field-access,
unqualifiedField
欄位的未限定參照
unused unusedArgument、unusedImport、unusedLabel、unusedLocal、unusedPrivate 和 unusedThrown 的巨集
unusedArgument 未使用的方法引數
unusedImport 未使用的匯入參照
unusedLabel 未用的標籤
unusedLocal 未使用的區域變數
unusedPrivate 未使用的私密成員宣告
unusedThrown 未使用的已宣告擲出異常狀況
uselessTypeCheck 不必要的 cast/instanceof 作業
varargsCast varargs 引數需要明確強制轉型
warningToken @SuppressWarnings 中無法處理的警告記號

-nowarn 沒有警告(相當於 -warn:none
-deprecation 相當於 -warn:deprecation
除錯選項
-g[:none|:lines,vars,source] 設定除錯屬性層次
-g 所有除錯資訊(相當於 -g:lines,vars,source
-g:none 沒有除錯資訊
-g:[lines,vars,source] 選擇性的除錯資訊
-preserveAllLocals 明確要求編譯器保留所有區域變數(用於除錯)。如果省略的話,編譯器會移除未用的區域變數。
忽略的選項(為了相容於 javac 選項)
-J<option> 將選項傳給虛擬機器
-X<option> 指定非標準選項。不會忽略 -Xemacs。
-X 列印非標準選項並結束
-O 最佳化執行時間
進階選項
@<file> 從檔案讀取指令行引數
-maxProblems <n> 每一編譯單元的問題數上限(預設值為 100)
-log <filename> 指定會在其中傾出來自編譯器的所有輸出的日誌檔。如果您要除錯批次編譯器,或取得含有來自批次建置之所有錯誤和警告的檔案, 這真的非常有用。如果副檔名是 .xml,產生的日誌便是 xml 檔。
-Xemacs 請利用 emacs 樣式,將錯誤和警告位置呈現在主控台和一般文字日誌中。 這個選項不會影響 XML 日誌。 當這個選項在作用中,下列訊息:
2. WARNING 在 /workspace/X.java 中
(在第 8 行)...

會呈現為:
/workspace/X.java:8: 警告:方法...
-proceedOnError 即使發生錯誤,也繼續編譯,並傾出含有問題方法或問題類型的類別檔。只有在您要即使有其它的錯誤,還是要能夠執行應用程式時, 才建議這樣做。
-verbose 列印主控台或日誌檔(如果指定)中,已存取/已處理的編譯單元。
-referenceInfo 計算參照資訊。這只有在連接到建置器時才有用。否則參照資訊並沒有用。
-progress 顯示進度(只適用於 -log 模式)。
-time 顯示速度資訊。
-noExit 編譯結束時不呼叫 System.exit(n)(如果沒有錯誤,則 n=0)。
-repeat <n> 重複編譯程序 <n> 次(效能分析)。
-inlineJSR 行入 JSR 位元組碼(如果目標 >= 1.5,便隱含表示)。
-enableJavadoc 考慮 javadoc 內的參照。
說明選項
-? -help 顯示說明訊息。
-v -version 顯示編譯器的建置號碼。這對報告錯誤非常有用。
-showversion 顯示編譯器的建置號碼並繼續進行。這對報告錯誤非常有用。

範例

d:\temp -classpath rt.jar -time -g -d d:/tmp 這會編譯 d:\temp 和其子資料夾中的所有程式檔。類別路徑只是 rt.jar。其會產生所有除錯屬性, 且所有產生的 .class 檔都會傾出至 d:\tmp 中。在批次程序完成後,即會顯示編譯器的速度。
d:\temp\Test.java -classpath d:\temp;rt.jar -g:none 它只會編譯 Test.java 及其相依檔案(如果有的話,會從 d:\temp 中擷取相依的檔案)。 類別路徑是 d:\temp,後面接著 rt.jar,這表示會先搜尋 d:\temp 中所有必要的類別,再搜尋 rt.jar。 它不會產生任何除錯屬性,且所有產生的 .class 檔都會傾出到 d:\temp 中。

使用 ant javac 配接器

您可使用 javac 配接器,而在 Ant Script 內使用 Eclipse 編譯器。 如果要使用 Eclipse 編譯器,您只需在 Script 中定義 build.compiler 內容即可。請見下列的小範例。
<?xml version="1.0" encoding="UTF-8"?>
<project name="compile" default="main" basedir="../.">

	<property name="build.compiler" value="org.eclipse.jdt.core.JDTCompilerAdapter"/>

	<property name="root" value="${basedir}/src"/>

	<property name="destdir" value="d:/temp/bin" />

	<target name="main">
		<javac srcdir="${root}" destdir="${destdir}" debug="on" nowarn="on" extdirs="d:/extdirs" source="1.4">
		    <classpath>
		      <pathelement location="${basedir}/../org.eclipse.jdt.core/bin"/>
		    </classpath>
		</javac>		
	</target>
</project>
有關 javac Ant 作業的語法,可在 Ant javac 作業文件中找到。現行配接器支援 Javac Ant 作業 1.4.1 到 1.6.5 版。

如果您使用的是 1.5.0 以上的版本,您可以使用巢狀編譯器引數元素來指定編譯器特定的選項。

...
		<javac srcdir="${root}" destdir="${destdir}" debug="on" nowarn="on" extdirs="d:/extdirs" source="1.4">
		    <classpath>
		      <pathelement location="${basedir}/../org.eclipse.jdt.core/bin"/>
		    </classpath>
    <compilerarg compiler="org.eclipse.jdt.core.JDTCompilerAdapter" line="-1.5 -warn:+boxing"/>
</javac>		
...

為了避免取得相依於編譯器的 Script,建議您使用設為 org.eclipse.jdt.core.JDTCompilerAdapter 的編譯器引數。 如果未如此設定,該 Script 只能用於 Eclipse 編譯器。如果設定,且其名稱與 build.compiler 內容所指定的編譯器名稱不同,則會忽略巢狀編譯器引數。

問題判斷

JDT Core 會定義一個 private 的標記(標記類型 "org.eclipse.jdt.core.problem"), 來表示編譯問題。如果要以程式設計的方式發掘編譯器所偵測到的問題,則應使用標準平台標記通訊協定 。 有關使用標記的概觀,請參閱資源標記

下列片段尋找一個編譯單元中的所有 Java 問題標記。

      public IMarker[] findJavaProblemMarkers(ICompilationUnit cu) 
         throws CoreException {
      IResource javaSourceFile = cu.getUnderlyingResource();
      IMarker[] markers = 
         javaSourceFile.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER,
            true, IResource.DEPTH_INFINITE);
   }

Java 問題標記由 Java 專案建置器所維護, 且會在解決問題和重新編譯 Java 程式檔之後自動移除。

問題 ID 值設為 IProblem 所定義的常數之一。 問題 ID 雖然可用, 但因訊息已本地化, 因此您可根據預設的語言環境加以變更。 定義在 IProblem 中的常數採自我說明方式。

您應定義 IProblemRequestor 的實作,以便在 Java 作業期間收集所發現的問題。 如果在建立工作副本期間有提供 IProblemRequestor, 則工作副本可和問題偵測維持一致。如果要如此做, 您可以使用 reconcile 方法。其範例如下:

  ICompilationUnit unit = ..; // get some compilation unit
			
  // create requestor for accumulating discovered problems
  IProblemRequestor problemRequestor = new IProblemRequestor() {
    public void acceptProblem(IProblem problem) {
      System.out.println(problem.getID() + ": " + problem.getMessage());
    }
    public void beginReporting() {}
    public void endReporting() {}
    public boolean isActive() {	return true; } // will detect problems if active
  };
    
  // use working copy to hold source with error
  ICompilationUnit workingCopy = unit.getWorkingCopy(new WorkingCopyOwner() {}, problemRequestor, null);
  ((IOpenable)workingCopy).getBuffer().setContents("public class X extends Zork {}");

  // trigger reconciliation			
  workingCopy.reconcile(NO_AST, true, null, null);
您可以在 acceptProblem(IProblem) 方法中針對所報告的問題新增一項動作。在本例中, 所報告的問題為 Zork 無法解析,或不是有效的 Super 類別, 且其 ID 為 IProblem.SuperclassNotFound

使用 SuppressWarnings 來排除警告

Java 5.0 可讓使用者以註解 java.lang.SuppressWarning 來停用編譯單元子集相關的編譯警告。

	@SuppressWarning("unused") public void foo() {
		String s;
	}

如果不使用註解,編譯器會指出區域變數 s 從未使用。 當使用註解時,編譯器在 foo 方法的區域範圍內,會自動忽略這個警告。 如此,相同編譯單元或相同專案的其他處仍然可以繼續發出警告。

SuppressWarning 註解內可用的記號如下: