编译 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 Core Batch Compiler 一节。此 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:无                         禁用所有警告
    -warn:<用 , 分隔的警告>    精确地启用列示的警告
    -warn:+<用 , 分隔的警告>   启用其他警告
    -w rn:-<用 , 分隔的警告>   禁用特定的警告
allDeprecation 建议不要使用的内容,即使在建议不要使用的代码中亦如此
allJavadoc javadoc 无效或缺少 javadoc
assertIdentifier 出现了用作标识的 assert
boxing 自动装箱转换
charConcat 在字符串并置中使用了字符型数组,但未显式地将其转换为字符串
conditionAssign 可能的意外布尔赋值
constructorName 具有构造函数名的方法
dep-ann 缺少 @Deprecated 注释
deprecation 在建议不要使用的代码外部使用了建议不要使用的类型或成员
discouraged 使用了与不鼓励使用的访问规则相匹配的类型
emptyBlock 未记录的空块
enumSwitch,
incomplete-switch
不完整的 enum 开关
fallthrough 可能的跳转 case
fieldHiding 字段隐藏了另一个变量
finalBound 带最终边界的类型参数
finally finally 块未正常结束
forbidden 使用了与被禁止的访问规则相匹配的类型
hiding 用于 fieldHiding、localHiding、typeHiding 和 maskedCatchBlock 的宏
indirectStatic 对静态成员的间接引用
intfAnnotation 用作超接口的注释类型
intfNonInherited 接口非继承方法兼容性
javadoc javadoc 无效
localHiding 局部变量隐藏了另一个变量
maskedCatchBlocks 隐藏的 catch 块
nls 非 nls 字符串文字(缺少标记 //$NON-NLS-<n>)
noEffectAssign 无作用的赋值
null 缺少 null 检查或 null 检查冗余
over-ann 缺少 @Override 注释
paramAssign 对参数赋值
pkgDefaultMethod 尝试覆盖包缺省方法
raw 原始类型(而不是参数化类型)的用法
semicolon 不必要的分号或空语句
serial 缺少 serialVersionUID
specialParamHiding 构造方法或 setter 参数隐藏了另一个字段
static-access indirectStatic 和 staticReceiver 的宏
staticReceiver 如果使用了非静态接收方来获取静态字段或调用静态方法
suppress 启用 @SuppressWarnings
syntheticAccess,
synthetic-access
当对内部类执行复合访问时
tasks 启用对源代码中的任务标记的支持
typeHiding 类型参数隐藏了另一个类型
unchecked 未校验的类型操作
unnecessaryElse 不必要的 else 子句
unqualified-field-access,
unqualifiedField
对字段的未限定引用
unused unusedArgument、unusedImport、unusedLabel、unusedLocal、unusedPrivate 和 unusedThrown 的宏
unusedArgument 未使用的方法参数
unusedImport 未使用的导入引用
unusedLabel 未使用的标签
unusedLocal 未使用的局部变量
unusedPrivate 未使用的私有成员声明
unusedThrown 未使用的声明的抛出异常
uselessTypeCheck 不必要的强制类型转换/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 in /workspace/X.java
(at line 8)...

显示为:
/workspace/X.java:8: warning: The method...
-proceedOnError 即使出现错误也继续编译,并转储包含问题方法或问题类型的类文件。仅当您希望即使未解决全部错误也能够运行应用程序时,才建议指定此选项。
-verbose 在控制台中(或者在日志文件中,如果指定的话)打印已访问的/已处理的编译单元。
-referenceInfo 计算引用信息。仅当已连接到构建器时,此选项才有用。否则,引用信息是没有用的。
-progress 显示进度(仅在 -log 方式下)。
-time 显示速度信息。
-noExit 编译结束时不调用 System.exit(n)(如果没有错误,则 n=0)。
-repeat <n> 将编译过程重复 <n> 次(性能分析)。
-inlineJSR 直接插入 JSR 字节码(如果 target >= 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 脚本内使用 Eclipse 编译器。为了使用 Eclipse 编译器,只需在脚本中定义 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>
可以在 Ant javac 任务文档中找到用于 javac Ant 任务的语法。当前适配器支持 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>		
...

为了防止脚本依赖于编译器,我们建议您将编译器参数设置为 org.eclipse.jdt.core.JDTCompilerAdapter。如果未设置此参数,则该脚本只能与 Eclipse 编译器配合使用。如果设置了该参数,并且名称与属性 build.compiler 指定的编译器名不同,则将忽略嵌套的编译器参数。

问题确定

JDT 核心定义了一个专用标记(标记类型为“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 源代码后,就会自动除去这些标记。

问题标识值设置为 IProblem 中定义的其中一个常量。问题标识是可靠的,但是消息是本地化的,因此会随缺省语言环境的不同而更改。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,或者它不是有效的超类,并且它的标识为 IProblem.SuperclassNotFound

使用 SuppressWarnings 来排除警告

Java 5.0 允许用户使用 java.lang.SuppressWarning 注释来禁用与一小部分编译单元相关的编译警告。

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

如果未指定此注释,编译器就会发出警告消息以指示从未使用局部变量 s。指定此注释后,编译器将在该 foo 方法局部以静默方式忽略此警告。这就可以保留同一编译单元或同一项目中其他位置的警告。

可以在 SuppressWarning 注释中使用的标记列表如下所示: