Javac - 编译器
您可以使用基础 JDK 工具和命令来创建和构建应用程序。以下部分介绍了可用于创建和构建应用程序的工具和命令。
介绍 javac
javac - 读取 Java 类和接口定义,并将它们编译成字节码和类文件。
javac [options] [sourcefiles]
选项 命令行选项。
源文件 要编译(例如 MyClass.java)或处理注释(例如 MyPackage.MyClass)的一个或多个源文件。
描述
javac
命令读取用 Java 编程语言编写的类和接口定义,并将它们编译成字节码类文件。javac
命令还可以处理 Java 源文件和类中的注释。
在 JDK 9 中引入了一个启动器环境变量 JDK_JAVAC_OPTIONS
,它将内容预先添加到命令行,以传递给 javac。
有两种方法可以将源代码文件名传递给 javac
对于少量源文件,您可以在命令行中列出文件名。
对于少量源文件,您可以在 javac 命令行中使用
@filename
选项来包含一个列出源文件名的文件。
源代码文件名必须具有 .java
后缀,类文件名必须具有 .class
后缀,并且源文件和类文件都必须具有标识类的根名称。例如,名为 MyClass 的类将写入名为 MyClass.java
的源文件中。
class MyClass {
public static void main(String[] args) {
System.out.println("this is my class");
}
}
并编译成名为 MyClass.class
的字节码类文件。
javac MyClass.java
MyClass.java
MyClass.class
内部类定义会生成额外的类文件。这些类文件的名称将组合内部类和外部类名称,例如 MyClass$MyInnerClass.class。
您应该将源文件排列在反映其包树的目录树中。例如
- Linux 和 macOS: 如果您的所有源文件都在
/workspace
中,则将com.mysoft.mypack.MyClass
的源代码放在/workspace/com/mysoft/mypack/MyClass.java
中。 - Windows: 如果您的所有源文件都在
\workspace
中,则将com.mysoft.mypack.MyClass
的源代码放在\workspace\com\mysoft\mypack\MyClass.java
中。
默认情况下,编译器将每个类文件放在与其源文件相同的目录中。您可以使用标准选项中描述的 -d
选项指定单独的目标目录。
编程接口
javac 命令支持由 javax.tools
包中的类和接口定义的新 Java 编译器 API。
隐式加载的源文件
要编译一组源文件,编译器可能需要隐式加载其他源文件。这些文件目前不受注释处理的约束。默认情况下,编译器在发生注释处理并且编译任何隐式加载的源文件时会发出警告。-implicit
选项提供了一种抑制警告的方法。
使用 JDK_JAVAC_OPTIONS 环境变量
JDK_JAVAC_OPTIONS
环境变量的内容(由空格
或空格字符(\n
、\t
、\r
或 \f
)分隔)将作为参数列表预先添加到传递给 javac
的命令行参数中。
环境变量的编码要求与系统上的 javac 命令行相同。JDK_JAVAC_OPTIONS
环境变量内容的处理方式与命令行中指定的方式相同。
单引号 '
或双引号 "
可用于括起包含空格字符的参数。打开引号和第一个匹配的关闭引号之间的所有内容都将通过简单地删除一对引号来保留。如果找不到匹配的引号,启动器将中止并显示错误消息。@files
受支持,因为它们是在命令行中指定的。但是,与 @files
中一样,不支持使用通配符。
export JDK_JAVAC_OPTIONS='@"C:\white spaces\argfile"'
export JDK_JAVAC_OPTIONS='"@C:\white spaces\argfile"'
export JDK_JAVAC_OPTIONS='@C:\"white spaces"\argfile'
javac 选项概述
编译器有一组标准选项和跨编译选项,这些选项在当前开发环境中受支持。编译器还具有一组非标准选项,这些选项特定于当前虚拟机和编译器实现,但将来可能会发生更改。非标准选项以 -X 开头。以下部分介绍了不同类型的 javac 选项。
- 标准选项
- javac 的跨编译选项
- 额外选项
标准选项
@filename
从文件读取选项和文件名。为了缩短或简化 javac
命令,您可以指定一个或多个包含 javac 命令参数的文件(除了 -J
选项)。这使您可以在任何操作系统上创建任意长度的 javac 命令。
-Akey[=value]
指定要传递给注释处理器的选项。这些选项不会被 javac
直接解释,而是可供各个处理器使用。键值应为一个或多个由点 .
分隔的标识符。
--add-modules module,module
指定除初始模块之外要解析的根模块,或者如果模块为 ALL-MODULE-PATH,则指定模块路径上的所有模块。
--boot-class-path path
或 -bootclasspath path
覆盖引导类文件的路径。
--class-path path
、-classpath path
或 -cp path
指定在何处查找用户类文件和注释处理器。此类路径将覆盖 CLASSPATH
环境变量中的用户类路径。
- 如果未指定
--class-path
、-classpath 或-cp
,则用户类路径为当前目录。 - 如果未指定
-sourcepath
选项,则还会在用户类路径中搜索源文件。 - 如果未指定
-processorpath
选项,则还会在类路径中搜索注释处理器。
-d directory
设置类文件的目标目录。如果类是包的一部分,则 javac
将类文件放在反映包名称的子目录中,并根据需要创建目录。例如
- Linux 和 macOS: 如果您指定
-d /home/myclasses
并且类名为com.mypackage.MyClass
,则类文件为/home/myclasses/com/mypackage/MyClass.class
。 - Windows: 如果您指定
-d C:\myclasses
并且类名为com.mypackage.MyClass
,则类文件为C:\myclasses\com\mypackage\MyClass.class
。
-deprecation
显示对已弃用成员或类的每次使用或覆盖的描述。如果没有 -deprecation 选项,javac 将显示使用或覆盖已弃用成员或类的源文件的摘要。-deprecation 选项是 -Xlint:deprecation 的简写。
--enable-preview
启用预览语言功能。与 -source 或 --release 结合使用。
-encoding encoding
指定源文件使用的字符编码,例如 EUC-JP 和 UTF-8。如果未指定 -encoding 选项,则使用平台默认编码。
-endorseddirs directories
覆盖认可标准路径的位置。
-extdirs directories
覆盖已安装扩展的位置。directories 变量是一个用冒号分隔的目录列表。将在指定的目录中的每个 JAR 文件中搜索类文件。找到的所有 JAR 文件都将成为类路径的一部分。
-g
生成所有调试信息,包括局部变量。默认情况下,只生成行号和源文件信息。
-g:[lines, vars, source]
只生成由逗号分隔的关键字列表指定的调试信息类型。有效的关键字是
`lines` - Line number debugging information.
`vars` - Local variable debugging information.
`source` - Source file debugging information.
-g:none
不生成调试信息。
-h directory
指定放置生成的本机头文件的位置。
当您指定此选项时,将为每个包含本机方法或具有一个或多个用 java.lang.annotation.Native 注释的常量的类生成一个本机头文件。如果类是包的一部分,则编译器将本机头文件放在反映包名称的子目录中,并根据需要创建目录。
--help, –help 或 -?
打印标准选项的概要。
--help-extra 或 -X
打印额外选项的帮助信息。
-implicit:[none, class]
指定是否为隐式引用的文件生成类文件。
-implicit:class
自动生成类文件。
-implicit:none
抑制类文件生成。
如果未指定此选项,则默认情况下会自动生成类文件。在这种情况下,如果在执行注释处理时也生成了任何类文件,则编译器会发出警告。当显式设置 -implicit
选项时,不会发出警告。请参阅搜索类型。
-Joption
将选项传递给运行时系统,其中 option 是 java
命令中描述的 Java 选项之一。例如,-J-Xms48m
将启动内存设置为 48 MB。
--limit-modules module,module*
限制可观察模块的范围。
--module module-name 或 -m module-name
只编译指定的模块并检查时间戳。
--module-path path 或 -p path
指定在何处查找应用程序模块。
--module-source-path module-source-path
指定在何处查找多个模块的输入源文件。
--module-version version
指定正在编译的模块的版本。
-nowarn
禁用警告消息。此选项与 -Xlint:none 选项的作用相同。
-parameters
为方法参数生成反射元数据。将构造函数和方法的形式参数名称存储在生成的类文件中,以便反射 API 中的 java.lang.reflect.Executable.getParameters 方法可以检索它们。
-proc:[none, only]
控制是否执行注释处理和编译。-proc:none
表示编译在没有注释处理的情况下进行。-proc:only 表示只执行注释处理,不进行任何后续编译。
-processor class1[,class2,class3...]
要运行的注释处理器的名称。这将绕过默认的发现过程。
--processor-module-path path
指定用于查找注释处理器的模块路径。
--processor-path path
或 -processorpath path
指定在何处查找注释处理器。如果未使用此选项,则将在类路径中搜索处理器。
-profile profile
检查指定的配置文件中是否可以使用所用的 API。
--release release
针对特定 VM 版本的公共、受支持和已记录的 API 进行编译。
指定用于放置生成的源文件的目录。如果一个类属于一个包,那么编译器会将源文件放在一个反映包名的子目录中,并根据需要创建目录。例如
- Linux 和 macOS:如果您指定
-s /home/mysrc
并且类名为com.mypackage.MyClass
,那么源文件将放在/home/mysrc/com/mypackage/MyClass.java
中。 - Windows:如果您指定
-s C:\mysrc
并且类名为com.mypackage.MyClass
,那么源文件将放在C:\mysrc\com\mypackage\MyClass.java
中。
-source release
指定接受的源代码版本。允许以下 release 值
-source 15
编译器接受在 Java SE 15 中引入的功能的代码。
-source 16
默认值。编译器接受在 Java SE 16 中引入的功能的代码。
--source-path path
或 -sourcepath path
指定在何处查找输入源文件。这是用于搜索类或接口定义的源代码路径。与用户类路径一样,源路径条目在 Linux 和 macOS 上用冒号 (:
) 分隔,在 Windows 上用分号 (;
) 分隔。它们可以是目录、JAR 存档或 ZIP 存档。如果使用包,那么目录或存档中的本地路径名必须反映包名。
--system jdk | none
覆盖系统模块的位置。
-target release
为特定 VM 版本生成类文件。
--upgrade-module—path path
覆盖可升级模块的位置。
-verbose
输出有关编译器正在执行的操作的消息。消息包括有关加载的每个类和编译的每个源文件的信息。
--version
或 -version
打印版本信息。
-Werror
当出现警告时终止编译。
javac 的跨编译选项
默认情况下,对于 JDK 9 之前的版本,类是针对与 javac 命令一起提供的平台的引导类进行编译的。但是 javac 也支持交叉编译,其中类是针对不同 Java 平台实现的引导类进行编译的。在交叉编译时,使用 -bootclasspath
和 -extdirs
选项非常重要。
额外选项
--add-exports module/package=other-module(,other-module)*
指定一个包,当 other-module 的值为 ALL-UNNAMED 时,该包将被视为从其定义模块导出到其他模块或所有未命名模块。
--add-reads module=other-module(,other-module)*
指定其他模块,这些模块将被视为由给定模块所需。
--default-module-for-created-files module-name
指定注释处理器创建的文件的回退目标模块,如果未指定或推断出任何模块。
-Djava.endorsed.dirs=dirs
覆盖认可标准路径的位置。
-Djava.ext.dirs=dirs
覆盖已安装扩展的位置。
--doclint-format [html4|html5]
指定文档注释的格式。
--patch-module module=file(:file)*
使用 JAR 文件或目录中的类和资源覆盖或增强模块。
-Xbootclasspath:path
覆盖引导类文件的位置。
-Xbootclasspath/a:path
在引导类路径中添加后缀。
-Xbootclasspath/p:path
在引导类路径中添加前缀。
-Xdiags:[compact, verbose]
选择诊断模式。
-Xdoclint
启用对 javadoc 注释中问题的推荐检查。
-Xdoclint:(all|none|[-]group)[/access]
启用或禁用特定组的检查。
group 可以具有以下值之一
accessibility
html
missing
reference
syntax
access 指定 -Xdoclint
选项检查的类和成员的最低可见性级别。它可以具有以下值之一(按从最可见到最不可见排序)
public
protected
package
private
默认访问级别为 private
。
有关这些检查组的更多信息,请参阅 javadoc 命令的 -Xdoclint
选项。-Xdoclint
选项在 javac 命令中默认情况下处于禁用状态。
例如,以下选项检查具有受保护和更高访问级别(包括受保护和公共)的类和成员(使用所有组的检查)
-Xdoclint:all/protected
-Xdoclint:all,-html/package
-Xdoclint/package:[-]packages(,[-]package)*
启用或禁用特定包中的检查。每个包要么是包的限定名称,要么是包名称前缀后跟一个句点和星号 (.*),它扩展到给定包的所有子包。每个包都可以用连字符 (-) 为前缀,以禁用对指定包或包的检查。
-Xlint
启用所有推荐的警告。在此版本中,建议启用所有可用的警告。
-Xlint:[-]key(,[-]key)*
提供要启用或禁用的警告,用逗号 (,) 分隔。在键前加上连字符 (-) 以禁用指定的警告。
key 的支持值是
all: Enables all warnings.
auxiliaryclass: Warns about an auxiliary class that’s hidden in a source file, and is used from other files.
cast: Warns about the use of unnecessary casts.
classfile: Warns about the issues related to classfile contents.
deprecation: Warns about the use of deprecated items.
dep-ann: Warns about the items marked as deprecated in javadoc but without the @Deprecated annotation.
divzero: Warns about the division by the constant integer 0.
empty: Warns about an empty statement after if.
exports: Warns about the issues regarding module exports.
fallthrough: Warns about the falling through from one case of a switch statement to the next.
finally: Warns about finally clauses that do not terminate normally.
module: Warns about the module system-related issues.
opens: Warns about the issues related to module opens.
options: Warns about the issues relating to use of command line options.
overloads: Warns about the issues related to method overloads.
overrides: Warns about the issues related to method overrides.
path: Warns about the invalid path elements on the command line.
processing: Warns about the issues related to annotation processing.
rawtypes: Warns about the use of raw types.
removal: Warns about the use of an API that has been marked for removal.
requires-automatic: Warns developers about the use of automatic modules in requires clauses.
requires-transitive-automatic: Warns about automatic modules in requires transitive.
serial: Warns about the serializable classes that do not provide a serial version ID. Also warns about access to non-public members from a serializable element.
static: Warns about accessing a static member using an instance.
try: Warns about the issues relating to the use of try blocks (that is, try-with-resources).
unchecked: Warns about the unchecked operations.
varargs: Warns about the potentially unsafe vararg methods.
none: Disables all warnings.
-Xmaxerrs number
设置要打印的最大错误数。
-Xmaxwarns number
设置要打印的最大警告数。
-Xpkginfo:[always, legacy, nonempty]
指定 javac 命令何时以及如何使用以下选项之一从 package-info.java 文件生成 package-info.class 文件
always - Generates a package-info.class file for every package-info.java file. This option may be useful if you use a build system such as Ant, which checks that each .java file has a corresponding .class file.
legacy - Generates a package-info.class file only if package-info.java contains annotations. This option doesn't generate a package-info.class file if package-info.java contains only comments.
nonempty - Generates a package-info.class file only if package-info.java contains annotations with RetentionPolicy.CLASS or RetentionPolicy.RUNTIME.
-Xplugin:name args
指定要运行的插件的名称和可选参数。
-Xprefer:[source, newer]
指定当为使用以下选项之一的隐式编译类找到源文件和类文件时,要读取哪个文件
`-Xprefer:newer` - Reads the newer of the source or class files for a type (default).
`-Xprefer:source` - Reads the source file. Use `-Xprefer:source` when you want to be sure that any annotation processors can access annotations declared with a retention policy of SOURCE.
-Xprint
打印指定类型的文本表示形式,用于调试目的。这不会执行注释处理或编译。输出的格式可能会更改。
-XprintProcessorInfo
打印有关请求处理器处理哪些注释的信息。
-XprintRounds
打印有关初始和后续注释处理轮次的信息。
-Xstdout filename
将编译器消息发送到指定的文件。默认情况下,编译器消息会发送到 System.err。
命令行参数文件
参数文件可以包含 javac
选项和源文件名,以任何组合方式。文件中的参数可以用空格或换行符分隔。如果文件名包含嵌入的空格,则将整个文件名放在双引号中。
参数文件中的文件名相对于当前目录,而不是相对于参数文件的位置。这些列表中不允许使用通配符 *
(例如,用于指定 *.java
)。不支持使用 at 符号 @
递归解释文件。-J
选项不受支持,因为它们传递给启动器,启动器不支持参数文件。
执行 javac 命令时,使用 at 符号 @
作为前导字符传递每个参数文件的路径和名称。当 javac
命令遇到以 at 符号 @
开头的参数时,它会将该文件的内容扩展到参数列表中。
示例
使用 javac @filename 的示例
单个参数文件 您可以使用名为 argfile 的单个参数文件来保存所有 javac 参数
javac @argfile
此参数文件可以包含以下两个参数文件示例中显示的两个文件的全部内容。
两个参数文件 您可以创建两个参数文件:一个用于 javac 选项,另一个用于源文件名。请注意,以下列表没有行延续字符。创建一个名为 options 的文件,其中包含以下内容
Linux 和 macOS
-d classes
-g
-sourcepath /java/pubs/ws/1.3/src/share/classes
Windows
-d classes
-g
-sourcepath C:\java\pubs\ws\1.3\src\share\classes
创建一个名为 classes 的文件,其中包含以下内容
MyClass1.java
MyClass2.java
MyClass3.java
然后,运行 javac 命令,如下所示
javac @options @classes
带有路径的参数文件 参数文件可以有路径,但文件中的任何文件名都相对于当前工作目录(而不是 path1 或 path2)
javac @path1/options @path2/classes
使用 -Xlint 键的示例
cast 警告不必要的和多余的强制转换,例如
String s = (String) "Hello!"
classfile 警告与类文件内容相关的问题。
deprecation 警告使用已弃用的项目。例如
java.util.Date myDate = new java.util.Date();
int currentDay = myDate.getDay();
方法 java.util.Date.getDay
自 JDK 1.1 以来已弃用。
dep-ann 警告使用 @deprecated Javadoc 注释但没有 @Deprecated 注释的项目,例如
/**
* @deprecated As of Java SE 7, replaced by {@link #newMethod()}
*/
public static void deprecatedMethod() { }
public static void newMethod() { }
divzero 警告除以常量整数 0,例如
int divideByZero = 42 / 0;
empty 警告 if 语句后的空语句,例如
class E {
void m() {
if (true) ;
}
}
fallthrough 检查 switch 块中的贯穿情况,并为找到的任何贯穿情况提供警告消息。贯穿情况是指 switch 块中的情况,除了块中的最后一种情况外,其代码不包含 break 语句,允许代码执行从该情况贯穿到下一种情况。例如,此 switch 块中 case 1 标签后的代码没有以 break 语句结尾
switch (x) {
case 1:
System.out.println("1");
// No break statement here.
case 2:
System.out.println("2");
}
如果在编译此代码时使用了 -Xlint:fallthrough
选项,那么编译器会发出有关可能贯穿到 case 的警告,并给出 case 的行号。
finally 警告无法正常完成的 finally 子句,例如
public static int m() {
try {
throw new NullPointerException();
} catch (NullPointerException(); {
System.err.println("Caught NullPointerException.");
return 1;
} finally {
return 0;
}
}
编译器会为此示例中的 finally
块生成警告。调用 int 方法时,它会返回 0 的值。finally
块在 try 块退出时执行。在此示例中,当控制权转移到 catch 块时,int 方法退出。但是,finally
块必须执行,因此它会执行,即使控制权已转移到方法之外。
options 警告与命令行选项的使用相关的问题。请参阅 javac 的交叉编译选项。
overrides 警告与方法覆盖相关的问题。例如,考虑以下两个类
public class ClassWithVarargsMethod {
void varargsMethod(String... s) { }
}
public class ClassWithOverridingMethod extends ClassWithVarargsMethod {
@Override
void varargsMethod(String[] s) { }
}
编译器会生成类似于以下内容的警告
warning: [override] varargsMethod(String[]) in ClassWithOverridingMethod
overrides varargsMethod(String...) in ClassWithVarargsMethod; overriding
method is missing '...'
当编译器遇到可变参数方法时,它会将可变参数形式参数转换为数组。在方法 ClassWithVarargsMethod.varargsMethod
中,编译器将可变参数形式参数 String... s
转换为形式参数 String[]
s,这是一个与方法 ClassWithOverridingMethod.varargsMethod
的形式参数匹配的数组。因此,此示例可以编译。
path 警告命令行中无效的路径元素和不存在的路径目录(关于类路径、源路径和其他路径)。此类警告无法使用 @SuppressWarnings 注释抑制。例如
Linux 和 macOS:
javac -Xlint:path -classpath /nonexistentpath Example.java
Windows:
javac -Xlint:path -classpath C:\nonexistentpath Example.java
processing 警告与注释处理相关的问题。当您有一个带有注释的类,并且您使用的注释处理器无法处理该类型的异常时,编译器会生成此警告。例如,以下是一个简单的注释处理器
import java.util.*;
import javax.annotation.processing.*;
import javax.lang.model.*;
import javaz.lang.model.element.*;
@SupportedAnnotationTypes("NotAnno")
public class AnnoProc extends AbstractProcessor {
public boolean process(Set<? extends TypeElement> elems, RoundEnvironment renv){
return true;
}
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
}
}
@interface Anno { }
@Anno
class AnnosWithoutProcessors { }
以下命令编译注释处理器 AnnoProc
,然后针对源文件 AnnosWithoutProcessors.java
运行此注释处理器
javac AnnoProc.java
javac -cp . -Xlint:processing -processor AnnoProc -proc:only AnnosWithoutProcessors.java
当编译器针对源文件 AnnosWithoutProcessors.java
运行注释处理器时,它会生成以下警告
warning: [processing] No processor claimed any of these annotations: Anno
要解决此问题,您可以将类 AnnosWithoutProcessors
中定义和使用的注释从 Anno
重命名为 NotAnno
。
rawtypes 警告关于对原始类型的未经检查的操作。以下语句会生成 rawtypes 警告
void countElements(List l) { ... }
以下示例不会生成 rawtypes 警告
void countElements(List<?> l) { ... }
List 是一个原始类型。但是,List<?>
是一个无界通配符参数化类型。因为 List 是一个参数化接口,所以始终指定其类型参数。在此示例中,List
形式参数使用无界通配符 ?
作为其形式类型参数,这意味着 countElements
方法可以接受 List
接口的任何实例化。
serial 警告关于可序列化类中缺少 serialVersionUID 定义。例如
public class PersistentTime implements Serializable
{
private Date time;
public PersistentTime() {
time = Calendar.getInstance().getTime();
}
public Date getTime() {
return time;
}
}
编译器会生成以下警告
warning: [serial] serializable class PersistentTime has no definition of
serialVersionUID
如果可序列化类没有显式声明名为 serialVersionUID
的字段,则序列化运行时环境会根据类的各个方面为该类计算一个默认的 serialVersionUID
值,如 Java 对象序列化规范中所述。但是,强烈建议所有可序列化类显式声明 serialVersionUID
值,因为计算 serialVersionUID
值的默认过程对类细节非常敏感,这些细节可能会因编译器实现而异。因此,这可能会在反序列化期间导致意外的 InvalidClassExceptions。为了保证在不同的 Java 编译器实现中一致的 serialVersionUID
值,可序列化类必须声明一个显式的 serialVersionUID
值。
static 警告与静态变量的使用相关的问题,例如
class XLintStatic {
static void m1() { }
void m2() { this.m1(); }
}
编译器会生成以下警告
warning: [static] static method should be qualified by type name,
XLintStatic, instead of by an expression
要解决此问题,您可以按如下方式调用静态方法 m1
XLintStatic.m1();
或者,您可以从方法 m1 的声明中删除 static 关键字。
try 警告与 try 块的使用相关的问题,包括 try-with-resources 语句。例如,以下语句会生成警告,因为在 try 块中声明的资源 ac 未使用
try ( AutoCloseable ac = getResource() ) { // do nothing}
unchecked 为 Java 语言规范强制执行的未经检查的转换警告提供更多详细信息,例如
List l = new ArrayList<Number>();
List<String> ls = l; // unchecked warning
在类型擦除期间,类型 ArrayList<Number>
和 List<String>
分别变为 ArrayList
和 List
。
ls
命令具有参数化类型 List<String>
。当 l
引用的 List 被分配给 ls 时,编译器会生成一个未经检查的警告。在编译时,编译器和 JVM 无法确定 l
是否引用 List<String>
类型。在这种情况下,l
不会引用 List<String>
类型。因此,会发生堆污染。
当 List
对象 l
(其静态类型为 List<Number>
)被分配给另一个 List
对象 ls
(具有不同的静态类型 List<String>
)时,就会发生堆污染情况。但是,编译器仍然允许此分配。它必须允许此分配以保持与不支持泛型的 Java SE 版本的向后兼容性。由于类型擦除,List<Number>
和 List<String>
都变为 List
。因此,编译器允许将具有原始类型 List
的对象 l
分配给对象 ls。
varargs 警告关于不安全使用可变参数 (varargs) 方法,特别是那些包含不可再现参数的方法,例如
public class ArrayBuilder {
public static <T> void addToList (List<T> listArg, T... elements) {
for (T x : elements) {
listArg.add(x);
}
}
}
不可再现类型是一种在运行时无法完全获得类型信息的类型。编译器为方法 ArrayBuilder.addToList
的定义生成以下警告
warning: [varargs] Possible heap pollution from parameterized vararg type T
当编译器遇到可变参数方法时,它会将可变参数形式参数转换为数组。但是,Java 编程语言不允许创建参数化类型的数组。在方法 ArrayBuilder.addToList
中,编译器将可变参数形式参数 T... elements
转换为形式参数 T[]
elements,这是一个数组。但是,由于类型擦除,编译器将可变参数形式参数转换为 Object[]
elements。因此,存在堆污染的可能性。
通过提供命令行参数进行编译的示例
要像提供命令行参数一样进行编译,请使用以下语法
JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
此示例将诊断信息写入标准输出流,并返回 javac 命令从命令行调用时将给出的退出代码。您可以使用 javax.tools.JavaCompiler
接口中的其他方法来处理诊断信息,控制从哪里读取和写入文件等等。
编译多个源文件的示例
此示例编译 greetings 包中的 Aloha.java
、GutenTag.java
、Hello.java
和 Hi.java
源文件。
Linux 和 macOS
% javac greetings/*.java
% ls greetings
Aloha.class GutenTag.class Hello.class Hi.class
Aloha.java GutenTag.java Hello.java Hi.java
Windows
C:\>javac greetings\*.java
C:\>dir greetings
Aloha.class GutenTag.class Hello.class Hi.class
Aloha.java GutenTag.java Hello.java Hi.java
指定用户类路径的示例
更改上一个示例中的一个源文件后,重新编译它
Linux 和 macOS
pwd
/examples
javac greetings/Hi.java
Windows
C:\>cd
\examples
C:\>javac greetings\Hi.java
因为 greetings.Hi
引用了 greetings 包中的其他类,所以编译器需要找到这些其他类。上一个示例之所以有效,是因为默认的用户类路径是包含包目录的目录。如果您想在不关心当前目录的情况下重新编译此文件,则通过设置 CLASSPATH 将示例目录添加到用户类路径。此示例使用 -classpath
选项。
Linux 和 macOS
javac -classpath /examples /examples/greetings/Hi.java
Windows
C:\>javac -classpath \examples \examples\greetings\Hi.java
If you change greetings.Hi to use a banner utility, then that utility also needs to be accessible through the user class path.
Linux 和 macOS
javac -classpath /examples:/lib/Banners.jar /examples/greetings/Hi.java
Windows
C:\>javac -classpath \examples;\lib\Banners.jar \examples\greetings\Hi.java
要执行 greetings 包中的类,程序需要访问 greetings 包,以及 greetings 类使用的类。
Linux 和 macOS
java -classpath /examples:/lib/Banners.jar greetings.Hi
Windows
C:\>java -classpath \examples;\lib\Banners.jar greetings.Hi
注释处理
javac 命令直接支持注释处理,取代了对单独的注释处理命令 apt 的需求。注释处理器的 API 定义在 javax.annotation.processing 和 javax.lang.model 包及子包中。
注释处理的工作原理
除非使用 -proc:none
选项禁用注释处理,否则编译器会搜索任何可用的注释处理器。搜索路径可以使用 -processorpath
选项指定。如果未指定路径,则使用用户类路径。处理器通过名为 META-INF/services/javax.annotation.processing
的服务提供者配置文件来定位。搜索路径上的处理器。此类文件应包含要使用的任何注释处理器的名称,每行列出一个。或者,可以使用 -processor 选项显式指定处理器。在扫描命令行上的源文件和类以确定存在哪些注释后,编译器会查询处理器以确定它们处理哪些注释。当找到匹配项时,就会调用处理器。处理器可以声明它处理的注释,在这种情况下,不会再尝试为这些注释查找任何处理器。在声明所有注释后,编译器不会再搜索其他处理器。
如果任何处理器生成新的源文件,则会进行另一轮注释处理:扫描任何新生成的源文件,并像以前一样处理注释。在先前轮次中调用的任何处理器也会在所有后续轮次中被调用。这将持续进行,直到不再生成新的源文件。在没有生成新的源文件的一轮之后,会最后一次调用注释处理器,以便它们有机会完成任何剩余的工作。最后,除非使用-proc:only
选项,否则编译器将编译原始文件和所有生成的源文件。
搜索类型
为了编译源文件,编译器通常需要有关类型的的信息,但类型定义不在命令行上指定的源文件中。编译器需要每个在源文件中使用、扩展或实现的类或接口的类型信息。这包括源文件中未明确提及的类和接口,但它们通过继承提供信息。
例如,当您创建 java.awt.Window 的子类时,您也在使用 Window 的祖先类:java.awt.Container、java.awt.Component
和 java.lang.Object
。当编译器需要类型信息时,它会搜索定义该类型的源文件或类文件。编译器首先在引导类和扩展类中搜索类文件,然后在用户类路径(默认情况下是当前目录)中搜索。用户类路径通过设置 CLASSPATH 环境变量或使用-classpath
选项来定义。
如果设置了-sourcepath
选项,则编译器会在指定的路径中搜索源文件。否则,编译器会在用户类路径中搜索类文件和源文件。您可以使用-bootclasspath
和-extdirs
选项指定不同的引导类或扩展类。
成功的类型搜索可能会生成类文件、源文件或两者。如果两者都找到,则可以使用-Xprefer
选项指示编译器使用哪个。如果指定了 newer,则编译器将使用这两个文件中的较新者。如果指定了 source,则编译器将使用源文件。默认值为 newer。
如果类型搜索找到所需类型的源文件,无论是单独找到还是由于-Xprefer
选项的设置而找到,则编译器会读取源文件以获取所需的信息。默认情况下,编译器还会编译源文件。您可以使用 -implicit 选项指定行为。如果未指定,则不会为源文件生成任何类文件。如果指定了 class,则会为源文件生成类文件。
编译器可能直到注释处理完成后才会发现对某些类型信息的需要。当类型信息在源文件中找到并且没有指定-implicit
选项时,编译器会发出警告,指出该文件正在编译,但没有经过注释处理。要禁用警告,请在命令行上指定文件(以便它会经过注释处理)或使用-implicit
选项指定是否应为这些源文件生成类文件。
上次更新: 2021 年 9 月 14 日