javac 命令

Name

javac - 读取 Java 声明并将它们编译成类文件

概要

javac [options ] [sourcefiles-or-classnames ]

options
命令行选项。
sourcefiles-or-classnames
要编译的源文件(例如 Shape.java )或要处理注释的先前编译类的名称(例如 geometry.MyShape )。

Description

javac 命令读取包含用 Java 编程语言编写的模块、包和类型声明的 source files,并将它们编译成在 Java 虚拟机上运行的 class files

javac 命令也可以在 Java 源文件和类中使用 过程注解

源文件的文件扩展名必须为 .java。类文件的文件扩展名为 .class。源文件和类文件通常都有标识内容的文件名。例如,名为 Shape 的类将在名为 Shape.java 的源文件中声明,并编译为名为 Shape.class 的类文件。

有两种方法可以将源文件指定为 javac

在命令行或参数文件中指定的源文件顺序并不重要。 javac 会将文件作为一个组编译在一起,并将自动解决各种源文件中声明之间的任何依赖关系。

javac 期望源文件排列在文件系统上的一个或多个目录层次结构中,如 源码整理 中所述。

要编译源文件,javac 需要找到源文件中的代码使用、扩展或实现的每个类或接口的声明。这让 javac 检查代码是否有权访问这些类和接口。您可以使用命令行选项告诉 javac 在哪里搜索它们的源文件,而不是明确指定这些类和接口的源文件。如果你以前编译过那些源文件,你可以使用选项告诉javac去哪里搜索相应的类文件。这些选项的名称都以“路径”结尾,在 标准选项 中进行了描述,并在 配置编译搜索模块、包和类型声明 中进行了进一步描述。

默认情况下,javac 将每个源文件编译为与源文件位于同一目录中的类文件。但是,建议使用 -d 选项指定一个单独的目标目录。

命令行 options环境变量 还控制 javac 如何执行各种任务:

javac 支持 为平台的早期版本编译,也可以使用多个 APIs 之一从 Java 代码调用

选项

javac 提供 标准选项额外选项 是非标准的或供高级使用的。

一些选项采用一个或多个参数。如果参数包含空格或其他空白字符,则应根据用于调用 javac 的环境约定引用该值。如果选项以单个破折号 (-) 开头,则参数应直接跟在选项名称之后,或者应该用冒号 (:) 或空格分隔,具体取决于选项。如果选项以双破折号 (--) 开头,则参数可以用空格分隔,也可以用不带额外空格的等号 (=) 字符分隔。例如,

-Aname="J. Duke"
-proc:only
-d myDirectory
--module-version 3
--module-version=3 

在以下选项列表中,参数 path 表示搜索路径,由由平台路径分隔符分隔的文件系统位置列表组成(在 Windows 上为分号 ;,在其他系统上为冒号 :。)取决于选项,文件系统位置可以是目录、JAR 文件或 JMOD 文件。

标准选项

@ filename
从文件中读取选项和文件名。要缩短或简化 javac 命令,您可以指定一个或多个文件,其中包含 javac 命令的参数(-J 选项除外)。这使您可以在任何操作系统上创建任何长度的 javac 命令。见命令行参数文件
-A key [= value ]
指定传递给注解处理器的选项。这些选项不由 javac 直接解释,但可供各个处理器使用。 key 值应该是由点 (.) 分隔的一个或多个标识符。
--add-modules module , module
指定除了初始模块之外要解析的根模块,或者如果 moduleALL-MODULE-PATH 则指定模块路径上的所有模块。
--boot-class-path path-bootclasspath path

重写引导程序类文件的位置。

Note: 这只能在为 JDK 9 之前的版本编译时使用。如适用,请参阅 --release -source -target 中的描述以获取详细信息。对于 JDK 9 或更高版本,请参阅 --system

--class-path path-classpath path-cp path

指定在哪里可以找到用户类文件和注解处理器。该类路径覆盖 CLASSPATH 环境变量中的用户类路径。

  • 如果未指定 --class-path-classpath-cp ,则用户类路径是 CLASSPATH 环境变量的值(如果已设置),否则为当前目录。

  • 如果不是为模块编译代码,如果未指定 --source-path 或 -sourcepath` 选项,那么也会在用户类路径中搜索源文件。

  • 如果未指定 -processorpath 选项,则还会在类路径中搜索注解处理器。

-d directory

为类文件设置目标目录(或 class output directory )。如果类是包的一部分,则javac 将类文件放在反映模块名称(如果适用)和包名称的子目录中。如果目录和任何必要的子目录尚不存在,将创建它们。

如果未指定 -d 选项,则 javac 会将每个类文件放在与生成它的源文件相同的目录中。

除了为多个模块编译代码时,类输出目录的内容将组织在包层次结构中。为多个模块编译代码时,输出目录的内容将组织在模块层次结构中,每个模块的内容在单独的子目录中,每个子目录组织为包层次结构。

Note: 为一个或多个模块编译代码时,将在搜索以前编译的类时自动检查类输出目录。当不为模块编译时,为了向后兼容,not 目录会自动检查以前编译的类,因此建议使用 --class-path 选项或以下选项之一将类输出目录指定为用户类路径上的位置之一它的替代形式。

-deprecation
显示已弃用成员或类的每次使用或覆盖的说明。如果没有 -deprecation 选项,javac 会显示使用或覆盖已弃用成员或类的源文件的摘要。 -deprecation 选项是 -Xlint:deprecation 的简写。
--enable-preview
启用预览语言功能。与 -source --release 结合使用。
-encoding encoding
指定源文件使用的字符编码,例如 EUC-JP 和 UTF-8。如果未指定 -encoding 选项,则使用平台默认转换器。
-endorseddirs directories

重写认可标准路径的位置。

Note: 这只能在为 JDK 9 之前的版本编译时使用。如适用,请参阅 --release -source -target 中的描述以获取详细信息。

-extdirs directories

覆盖已安装扩展的位置。 directories 是目录列表,由平台路径分隔符分隔(在 Windows 上为 ;,否则为 :)。在指定目录中的每个 JAR 文件中搜索类文件。找到的所有 JAR 文件都成为类路径的一部分。

如果您正在为支持扩展机制的平台版本进行编译,则此选项指定包含扩展类的目录。请参阅[为平台的其他版本编译]。

Note: 这只能在为 JDK 9 之前的版本编译时使用。如适用,请参阅 --release -source -target 中的描述以获取详细信息。

-g
生成所有调试信息,包括局部变量。默认情况下,只生成行号和源文件信息。
-g: [lines , vars , source ]

仅生成由逗号分隔的关键字列表指定的调试信息种类。有效关键字是:

lines
行号调试信息。
vars
局部变量调试信息。
source
源文件调试信息。
-g:none
不生成调试信息。
-h directory

指定放置生成的本机头文件的位置。

当您指定此选项时,将为每个包含本机方法或具有一个或多个用 java.lang.annotation.Native 注释进行注释的常量的类生成一个本机头文件。如果该类是包的一部分,则编译器会将本机头文件放在反映模块名称(如果适用)和包名称的子目录中。如果目录和任何必要的子目录尚不存在,将创建它们。

--help-help-?
打印标准选项的概要。
--help-extra-X
打印一组额外选项的概要。
--help-lint
打印 -Xlint 选项支持的密钥。
-implicit: [没有任何 , ]

指定是否为隐式引用的文件生成类文件:

  • -implicit:class --- 自动生成类文件。

  • -implicit:none --- 抑制类文件生成。

如果没有指定这个选项,那么默认自动生成类文件。在这种情况下,如果在进行注释处理时生成任何类文件,编译器会发出警告。显式设置 -implicit 选项时不会发出警告。参见 搜索模块、包和类型声明

-J option

option 传递给运行时系统,其中 optionjava 命令中描述的 Java 选项之一。例如,-J-Xms48m 将启动内存设置为 48 MB。

Note: CLASSPATH 环境变量、-classpath 选项、-bootclasspath 选项和 -extdirs 选项未指定用于运行 javac 的类。尝试使用这些选项和变量自定义编译器实现是有风险的,而且通常无法实现您想要的结果。如果您必须自定义编译器实现,请使用 -J 选项将选项传递给底层 Java 启动器。

--limit-modules module , module *
限制可观察模块的范围。
--module module-name (, module-name )* 或 -m module-name (, 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: [没有任何 , 仅有的 ]
控制是否完成注释处理和编译。 -proc:none 表示编译在没有注释处理的情况下进行。 -proc:only表示只做注解处理,不做任何后续编译。
-processor class1 [, class2 , class3 ...]
要运行的注解处理器的名称。这会绕过默认的发现过程。
--processor-module-path path
指定用于查找注解处理器的模块路径。
--processor-path path-processorpath path
指定在哪里可以找到注解处理器。如果未使用此选项,则会在类路径中搜索处理器。
-profile profile

检查所使用的 API 在指定的配置文件中是否可用。此选项已弃用,可能会在未来的版本中删除。

Note: 这只能在为 JDK 9 之前的版本编译时使用。如适用,请参阅 --release -source -target 中的描述以获取详细信息。

--release release

根据指定 Java SE 版本的 Java 编程语言规则编译源代码,生成针对该版本的类文件。针对指定版本的组合 Java SE 和 JDK API 编译源代码。

release 的受支持值是当前 Java SE 版本和有限数量的以前版本,在命令行帮助中有详细说明。

对于当前版本,Java SE API 由版本中的 Java SE 模块导出的 java.*javax.*org.* 包组成; JDK API 由版本中 JDK 模块导出的 com.*jdk.* 包,以及版本中标准但非 Java SE 模块导出的 javax.* 包组成。

对于以前的版本,Java SE API 和 JDK API 是在该版本中定义的。

Note: 使用 --release 时,不能同时使用 --source /-source--target /-target 选项。

Note: 当使用 --release 指定支持 Java 平台模块系统的版本时,--add-exports 选项不能用于扩大指定版本中 Java SE、JDK 和标准模块导出的包集。

-s directory

指定用于放置生成的源文件的目录。如果类是包的一部分,则编译器会将源文件放在反映模块名称(如果适用)和包名称的子目录中。如果目录和任何必要的子目录尚不存在,将创建它们。

除了为多个模块编译代码时,源输出目录的内容将组织在包层次结构中。为多个模块编译代码时,源输出目录的内容将组织在模块层次结构中,每个模块的内容在单独的子目录中,每个子目录组织为包层次结构。

--source release-source release

根据指定 Java SE 版本的 Java 编程语言规则编译源代码。 release 的受支持值是当前 Java SE 版本和有限数量的以前版本,在命令行帮助中有详细说明。

如果未指定该选项,则默认为根据当前 Java SE 版本的 Java 编程语言规则编译源代码。

--source-path path-sourcepath path

指定在何处查找源文件。除非同时编译多个模块,否则这是用于搜索类或接口定义的源代码路径。

Note: 通过类路径找到的类可能会在它们的源文件也找到时被重新编译。参见 搜索模块、包和类型声明

--system jdk | none
重写系统模块的位置。
--target release-target release

生成适用于指定 Java SE 版本的 class 文件。 release 的受支持值是当前 Java SE 版本和有限数量的以前版本,在命令行帮助中有详细说明。

Note:目标版本必须等于或高于源版本。 (见 --source 。)

--upgrade-module-path path
重写可升级模块的位置。
-verbose
输出有关编译器正在做什么的消息。消息包括关于每个加载的类和编译的每个源文件的信息。
--version-version
打印版本信息。
-Werror
出现警告时终止编译。

额外选项

--add-exports module / = 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

重写认可标准路径的位置。

Note: 这只能在为 JDK 9 之前的版本编译时使用。如适用,请参阅 --release -source -target 中的描述以获取详细信息。

-Djava.ext.dirs= dirs

重写已安装扩展的位置。

Note: 这只能在为 JDK 9 之前的版本编译时使用。如适用,请参阅 --release -source -target 中的描述以获取详细信息。

--patch-module module = path
使用 JAR 文件或目录中的类和资源重写或扩充模块。
-Xbootclasspath: path

重写引导程序类文件的位置。

Note: 这只能在为 JDK 9 之前的版本编译时使用。如适用,请参阅 --release -source -target 中的描述以获取详细信息。

-Xbootclasspath/a: path

向引导程序类路径添加后缀。

Note: 这只能在为 JDK 9 之前的版本编译时使用。如适用,请参阅 --release -source -target 中的描述以获取详细信息。

-Xbootclasspath/p: path

向引导程序类路径添加前缀。

Note: 这只能在为 JDK 9 之前的版本编译时使用。如适用,请参阅 --release -source -target 中的描述以获取详细信息。

-Xdiags: [compact , verbose ]
选择诊断模式。
-Xdoclint
启用对文档注释中问题的建议检查。
-Xdoclint: (all |没有任何 |[- ]group )[/ 使用权 ]

在文档注释中启用或禁用特定的检查组。

group 可以具有以下值之一:accessibilityhtmlmissingreferencesyntax

变量 access 指定 -Xdoclint 选项检查的类和成员的最低可见性级别。它可以具有以下值之一(从最不可见到最不可见的顺序):publicprotectedpackageprivate

默认的 access 级别是 private

当以 doclint: 为前缀时,group 名称和 all 可以与 @SuppressWarnings 一起使用,以抑制有关正在编译的代码部分中的文档注释的警告。

有关这些检查组的更多信息,请参阅 javadoc 命令文档的 DocLint 部分。 -Xdoclint 选项在 javac 命令中默认禁用。

例如,以下选项检查访问级别为 protected 和更高(包括 protected 和 public)的类和成员(包括所有检查组):

-Xdoclint:all/protected

以下选项启用所有访问级别的所有检查组,除了它不会检查具有 package 和更高访问级别(包括 package、protected 和 public)的类和成员的 HTML 错误:

-Xdoclint:all,-html/package

-Xdoclint/package: [- ]packages (, [- ] )*

启用或禁用特定包中的检查。每个 package 是包的限定名称或包名称前缀后跟 .* ,它扩展到给定包的所有子包。每个 package 都可以使用连字符 (-) 作为前缀,以禁用对一个或多个指定包的检查。

有关详细信息,请参阅 javadoc 命令文档的 DocLint 部分。

-Xlint
启用所有推荐的警告。在此版本中,建议启用所有可用警告。
-Xlint: [- ]key (, [- ]key )*

提供启用或禁用的警告,以逗号分隔。在键前加上连字符 (-) 以禁用指定的警告。

key 支持的值为:

  • all:启用所有警告。

  • auxiliaryclass :警告隐藏在源文件中并被其他文件使用的辅助类。

  • cast:警告使用不必要的强制转换。

  • classfile:警告与类文件内容相关的问题。

  • deprecation :警告使用已弃用的项目。

  • dep-ann:警告在 javadoc 中标记为已弃用但没有 @Deprecated 注释的项目。

  • divzero:警告除以常数整数 0。

  • empty:警告 if 之后的空语句。

  • exports:警告有关模块导出的问题。

  • fallthrough:警告从 switch 语句的一个案例到下一个案例。

  • finally :对未正常终止的 finally 子句发出警告。

  • lossy-conversions:警告复合赋值中可能存在的有损转换。

  • missing-explicit-ctor:警告导出包中公共类和受保护类中缺少显式构造函数。

  • module:警告与模块系统相关的问题。

  • opens:警告与模块打开相关的问题。

  • options:警告有关使用命令行选项的问题。

  • overloads :警告与方法重载相关的问题。

  • overrides:警告与方法覆盖相关的问题。

  • path:警告命令行上的无效路径元素。

  • preview:警告有关预览语言功能的使用。

  • processing:警告与注释处理相关的问题。

  • rawtypes:警告原始类型的使用。

  • removal:警告有关已标记为删除的 API 的使用。

  • requires-automatic:警告开发人员在 requires 子句中使用自动模块。

  • requires-transitive-automatic:警告有关需要传递的自动模块。

  • serial:警告不提供序列版本 ID 的可序列化类。还警告从可序列化元素访问非公共成员。

  • static:警告使用实例访问静态成员。

  • strictfp :警告不必要使用 strictfp 修饰符。

  • synchronization:警告有关基于值的类实例的同步尝试。

  • text-blocks:警告文本块缩进中不一致的空白字符。

  • try:警告有关使用 try 块(即 try-with-resources)的问题。

  • unchecked:警告未经检查的操作。

  • varargs :警告可能不安全的 vararg 方法。

  • none:禁用所有警告。

除了 allnone 之外,这些键可以与 @SuppressWarnings 注释一起使用,以抑制正在编译的部分源代码中的警告。

参见 使用示例 - Xlint键

-Xmaxerrs number
设置要打印的最大错误数。
-Xmaxwarns number
设置要打印的最大警告数。
-Xpkginfo: [总是 , legacy , nonempty ]

指定 javac 命令何时以及如何使用以下选项之一从 package-info.java 文件生成 package-info.class 文件:

always
为每个 package-info.java 文件生成一个 package-info.class 文件。如果您使用 Ant 等构建系统,此选项可能会有用,它会检查每个 .java 文件是否具有相应的 .class 文件。
legacy

仅当 package-info.java 包含注释时才生成 package-info.class 文件。如果 package-info.java 仅包含注释,则此选项不会生成 package-info.class 文件。

Note: 可能会生成一个 package-info.class 文件,但如果 package-info.java 文件中的所有注释都具有 RetentionPolicy.SOURCE 则该文件为空。

nonempty
仅当 package-info.java 包含带 RetentionPolicy.CLASSRetentionPolicy.RUNTIME 的注释时才生成 package-info.class 文件。
-Xplugin: name args
指定要运行的插件的名称和可选参数。如果提供了 args,则应引用 nameargs 或以其他方式转义名称和所有参数之间的空白字符。有关插件 API 的详细信息,请参阅 jdk.compiler/com.sun.source.util.Plugin 的 API 文档。
-Xprefer: [source , newer ]

使用以下选项之一为隐式编译类找到源文件和类文件时,指定要读取的文件。参见 搜索模块、包和类型声明

  • -Xprefer:newer :读取类型的较新的源文件或类文件(默认)。

  • -Xprefer:source:读取源文件。当您想要确保任何注解处理器都可以访问使用 SOURCE 保留策略声明的注解时,请使用 -Xprefer:source

-Xprint
出于调试目的打印指定类型的文本表示。这不执行注释处理或编译。输出的格式可能会改变。
-XprintProcessorInfo
打印有关要求处理器处理哪些注释的信息。
-XprintRounds
打印有关初始和后续注释处理轮次的信息。
-Xstdout filename
将编译器消息发送到指定文件。默认情况下,编译器消息转到 System.err

环境变量

CLASSPATH

如果未指定 --class-path 选项或其任何替代形式,则类路径将默认为设置了 CLASSPATH 环境变量的值。但是,建议应设置此环境变量 not,并在需要时使用 --class-path 选项为类路径提供显式值。

JDK_JAVAC_OPTIONS

JDK_JAVAC_OPTIONS 环境变量的内容,由空格 ( ) 或空格字符(\n\t\r\f)分隔,作为参数列表传递给 javac 的命令行参数。

环境变量的编码要求与系统上的javac命令行相同。 JDK_JAVAC_OPTIONS 环境变量内容的处理方式与命令行中指定的方式相同。

单引号 (') 或双引号 (") 可用于将包含空白字符的参数括起来。只需删除引号对,即可保留开引号和第一个匹配的闭引号之间的所有内容。如果找不到匹配的报价,启动器将中止并显示一条错误消息。 @ files 受支持,因为它们在命令行中指定。但是,与 @ files 一样,不支持使用通配符。

Examples of quoting arguments containing white spaces:

export JDK_JAVAC_OPTIONS='@"C:\white spaces\argfile"'

export JDK_JAVAC_OPTIONS='"@C:\white spaces\argfile"'

export JDK_JAVAC_OPTIONS='@C:\"white spaces"\argfile'

命令行参数文件

参数文件可以包含命令行选项和源文件名的任意组合。文件中的参数可以用空格或换行符分隔。如果文件名包含嵌入空格,则将整个文件名放在双引号中。

参数文件中的文件名是相对于当前目录的,而不是参数文件的位置。这些列表中不允许使用通配符 (*)(例如用于指定 *.java)。不支持使用 at 符号 (@) 递归解释文件。不支持 -J 选项,因为它们被传递到不支持参数文件的启动器。

执行javac命令时,以at符号(@)前导字符传入每个参数文件的路径和名称。当 javac 命令遇到以 at 符号 (@) 开头的参数时,它会将该文件的内容扩展到参数列表中。

使用 javac 的示例@文件名

单参数文件

您可以使用名为 argfile 的单个参数文件来保存所有 javac 参数:

javac @argfile

此参数文件可以包含以下 Two Argument Files 示例中显示的两个文件的内容。

两个参数文件

您可以创建两个参数文件:一个用于 javac 选项,另一个用于源文件名。请注意,以下列表没有行继续符。

创建一个名为 options 的文件,其中包含以下内容:

Linux and 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 

创建一个名为 sources 的文件,其中包含以下内容:

MyClass1.java
MyClass2.java
MyClass3.java 

然后,运行 javac 命令如下:

javac @options @sources

带路径的参数文件

参数文件可以有路径,但文件内的任何文件名都是相对于当前工作目录的(不是 path1path2 ):

javac @path1/options @path2/sources

源码整理

在Java语言中,类和接口可以组织成包,包又可以组织成模块。 javac 期望文件系统目录中源文件的物理排列将类的组织映射到包中,并将包映射到模块中。

模块名称和包名称以小写字母开头,类名称以大写字母开头是一种广泛采用的约定。

一个包的源代码的安排

当类和接口被组织成一个包时,包被表示为一个目录,而任何子包都被表示为子目录。

例如:

在目录或子目录中,.java 文件表示相应包或子包中的类和接口。

例如:

在某些情况下,将代码拆分到单独的目录中很方便,每个目录的结构都如上所述,目录的聚合列表指定为 javac

模块源代码的安排

在 Java 语言中,模块是一组为重用而设计的包。除了用于类和接口的 .java 文件之外,每个模块都有一个名为 module-info.java 的源文件,其中:

  1. 声明模块的名称;

  2. 列出模块导出的包(以允许其他模块重用);

  3. 列出模块所需的其他模块(重用它们导出的包)。

当包被组织成一个模块时,该模块由一个或多个目录表示,该目录表示模块中的包,其中一个目录包含 module-info.java 文件。使用以模块命名的单个目录来包含 module-info.java 文件和代表模块中包的目录树(即上述的 package hierarchy)可能很方便,但不是必需的。模块源代码的确切排列通常由开发环境 (IDE) 或构建系统采用的约定决定。

例如:

开发环境可能会在为模块命名的目录和 javac 读取的源文件之间规定一些目录层次结构。

例如:

配置编译

本节介绍如何配置 javac 以执行基本编译。

有关在编译支持模块的平台版本时使用的更多详细信息,请参阅 配置模块系统

源文件

如果没有编译错误,相应的类文件将放在 输出目录 中。

某些系统可能会限制您可以在命令行上输入的数量;要解决这些限制,您可以使用 参数文件

为模块编译代码时,您还可以使用 --module -m 选项间接指定源文件。

输出目录

这通常会组织在 包层级 中,除非您从多个模块编译源代码,在这种情况下它将组织为 模块层级

编译完成后,如果要编译一个或多个模块,可以将输出目录放在 Java 发射器 的模块路径上;否则,您可以将输出目录放在 Java 启动器的类路径中。

预编译代码

要编译的代码可能会引用超出平台提供的库。如果是这样,您必须将这些库放在类路径或模块路径上。如果库代码不在模块中,则将其放在类路径中;如果它在模块中,请将其放在模块路径中。

Note:类路径和模块路径的选项并不相互排斥,尽管在为一个或多个模块编译代码时指定类路径并不常见。

其他源文件

要编译的代码可能会引用未在命令行中指定的其他源文件中的类型。如果是这样,您必须将这些源文件放在源路径或模块路径中。您只能指定以下选项之一:如果您不是为一个模块编译代码,或者如果您只是为单个模块编译代码,则使用源路径;如果您正在为多个模块编译代码,请使用模块源路径。

如果您希望能够引用其他源文件中的类型但不希望它们被编译,请使用 -implicit 选项。

Note :如果您正在为多个模块编译代码,则必须始终指定模块源路径,并且在命令行上指定的所有源文件必须位于模块源路径上的目录之一或其子目录中。

编译多个源文件的示例

此示例编译 greetings 包中的 Aloha.javaGutenTag.javaHello.javaHi.java 源文件。

Linux and 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 and macOS:

pwd
/examples
javac greetings/Hi.java 

Windows:

C:\>cd
\examples
C:\>javac greetings\Hi.java 

因为 greetings.Hi 引用了 greetings 包中的其他类,所以编译器需要找到这些其他类。前面的示例之所以有效,是因为默认的用户类路径是包含包目录的目录。如果您想重新编译此文件而不关心您所在的目录,请通过设置 CLASSPATH 将示例目录添加到用户类路径。此示例使用 -classpath 选项。

Linux and macOS:

javac -classpath /examples /examples/greetings/Hi.java

Windows:

C:\>javac -classpath \examples \examples\greetings\Hi.java

如果您将 greetings.Hi 更改为使用横幅实用程序,则该实用程序也需要可通过用户类路径访问。

Linux and 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 and macOS:

java -classpath /examples:/lib/Banners.jar greetings.Hi

Windows:

C:\>java -classpath \examples;\lib\Banners.jar greetings.Hi

配置模块系统

如果要在编译中包含其他模块,请使用 --add-modules 选项。当您编译不在模块中或自动模块中的代码时,这可能是必要的,并且代码引用附加模块中的 API。

如果要限制编译中的模块集,请使用 --limit-modules 选项。如果您想确保正在编译的代码能够在安装了一组有限模块的系统上运行,这可能很有用。

如果要打破封装并指定应将其他包视为从模块导出,请使用 --add-exports 选项。这在执行白盒测试时可能很有用;强烈建议不要在生产代码中依赖对内部 API 的访问。

如果要指定附加包应被视为模块所需,请使用 --add-reads 选项。这在执行白盒测试时可能很有用;强烈建议不要在生产代码中依赖对内部 API 的访问。

您可以使用 --patch-module 选项将附加内容修补到任何模块中。有关详细信息,请参阅 [修补模块]。

搜索模块、包和类型声明

要编译源文件,编译器通常需要有关模块或类型的信息,但声明不在命令行指定的源文件中。

javac 需要源文件中使用、扩展或实现的每个类或接口的类型信息。这包括源文件中未明确提及但通过继承提供信息的类和接口。

例如,当您创建 java.awt.Window 的子类时,您还使用了 Window 的祖先类:java.awt.Containerjava.awt.Componentjava.lang.Object

为模块编译代码时,编译器还需要具有该模块的可用声明。

成功的搜索可能会生成类文件、源文件或两者。如果两者都找到,那么您可以使用 -Xprefer 选项来指示编译器使用哪个。

如果搜索找到并使用源文件,则默认情况下javac 编译该源文件。可以使用 -implicit 更改此行为。

在注释处理完成之前,编译器可能不会发现对某些类型信息的需求。当在源文件中找到类型信息且未指定-implicit 选项时,编译器会发出警告,指出该文件正在编译而未进行注释处理。要禁用警告,请在命令行上指定文件(以便对其进行注释处理)或使用 -implicit 选项指定是否应为此类源文件生成类文件。

javac 定位这些类型声明的方式取决于模块代码中是否存在引用。

搜索面向包的路径

当在由面向包的位置组成的路径上搜索源文件或类文件时,javac 将依次检查路径上的每个位置以查找可能存在的文件。特定文件的首次出现会隐藏(隐藏)任何后续出现的同名文件。此阴影不影响对具有不同名称的任何文件的任何搜索。这在搜索源文件时会很方便,这些文件可能在不同的位置分组,例如共享代码、特定于平台的代码和生成的代码。在将类文件的替代版本注入包、调试或其他检测原因时,它也很有用。但是,它也可能是危险的,例如将不兼容的不同版本的库放在类路径上时。

搜索面向模块的路径

在为任何包或类型声明扫描任何模块路径之前,javac 将懒惰地扫描以下路径和位置以确定将在编译中使用的模块。

对于任何模块,该模块在扫描期间的第一次出现完全遮蔽(隐藏)了类似名称模块的任何后续出现。在定位模块时,javac 能够确定模块导出的包,并为每个模块关联模块内容的包导向路径。对于任何以前编译的模块,此路径通常是一个目录或提供内部类似目录层次结构的文件(例如 JAR 文件)的单个条目。因此,当搜索已知由模块导出的包中的类型时,javac 可以直接有效地定位声明。

搜索模块的声明

如果模块之前已经编译过,则模块声明位于模块内容的包层次结构根目录中名为 module-info.class 的文件中。

如果该模块是当前正在编译的模块之一,则模块声明将是类输出目录中模块的包层次结构根目录中名为 module-info.class 的文件,或者是源代码中某个位置中名为 module-info.java 的文件路径或模块的模块源路径之一。

当引用不在模块中时搜索类型声明

在搜索不在模块中的代码中引用的类型时,javac 将在以下位置查找:

在类路径和/或源路径上查找类型时,如果同时找到已编译的类文件和源文件,则默认情况下将使用最近修改的文件。如果源文件较新,它将被编译并且可能会覆盖该文件的任何先前编译版本。您可以使用 -Xprefer 选项覆盖默认行为。

当引用在模块中时搜索类型声明

在模块中搜索代码中引用的类型时,javac 将检查封闭模块的声明,以确定该类型是否位于从另一个模块导出的包中,该包可由封闭模块读取。如果是这样,javac将简单直接地去那个模块的定义中找到所需类型的定义。除非该模块是正在编译的另一个模块,javac 将只查找已编译的类文件文件。换句话说,javac 不会在模块路径上的平台模块或模块中寻找源文件。

如果被引用的类型不在其他可读模块中,javac 将检查正在编译的模块以尝试找到该类型的声明。 javac 将查找类型声明如下:

目录层次结构

javac 通常假定源文件和已编译的类文件将组织在文件系统目录层次结构中或组织在支持内部目录层次结构的文件类型中,例如 JAR 文件。支持三种不同的层次结构:package hierarchymodule hierarchymodule source hierarchy

虽然 javac 对源代码的组织相当宽松,但超出了源代码将组织在一个或包层次结构中的预期,并且通常可以适应开发环境和构建工具、一般 Java 工具以及 javac 和 Java 启动器规定的组织特别是,在编译类文件的组织方面更加严格,并且将在适当的情况下以包层次结构或模块层次结构进行组织。

这些层次结构的位置通过命令行选项指定为 javac,其名称通常以“路径”结尾,如 --source-path --class-path 。同样作为一般规则,名称包含单词 module 的路径选项(如 --module-path )用于指定模块层次结构,尽管一些与模块相关的路径选项允许在每个模块的基础上指定包层次结构。所有其他路径选项都用于指定包层次结构。

包层次结构

在一个包层次结构中,目录和子目录用于表示包名的组成部分,一个类型的源文件或编译后的类文件作为一个扩展名为.java.class的文件存储在最嵌套的目录中。

例如,在包层次结构中,类 com.example.MyClass 的源文件将存储在文件 com/example/MyClass.java

模块层次结构

在模块层次结构中,第一级目录以层次结构中的模块命名;在每个目录中,模块的内容都按包层次结构进行组织。

例如,在模块层次结构中,名为 my.library 的模块中名为 com.example.MyClass 的类型的已编译类文件将存储在 my.library/com/example/MyClass.class 中。

javac 使用的各种输出目录(类输出目录、源代码输出目录和本机头文件输出目录)在编译多个模块时都将组织在模块层次结构中。

模块源层次结构

虽然每个单独模块的源代码应该始终组织在包层次结构中,但将这些层次结构分组到模块源层次结构中可能会很方便。这类似于模块层次结构,只是在模块目录和作为模块源代码的包层次结构的根目录之间可能存在中间目录。

例如,在模块源层次结构中,名为 my.library 的模块中名为 com.example.MyClass 的类型的源文件可能存储在诸如 my.library/src/main/java/com/example/MyClass.java 的文件中。

模块源路径选项

--module-source-path 选项有两种形式:一个 module-specific form ,其中为每个包含要编译的代码的模块提供了一个包路径,以及一个 module-pattern 形式,其中每个模块的源路径由一个模式指定。当只涉及少量模块时,特定于模块的形式通常更易于使用;当模块数量很多并且模块以可以用模式描述的规则方式组织时,模块模式形式可能更方便。

可以给出 --module-source-path 选项的多个实例,每个实例使用模块模式形式或模块特定形式,但要遵守以下限制:

如果特定于模块的形式用于任何模块,则关联的搜索路径将重写可能从模块模式形式推断出的任何路径。

特定于模块的形式

特定于模块的形式允许为任何特定模块提供明确的搜索路径。这个表格是:

路径分隔符在 Windows 上是 ;,否则是 :

Note: 这类似于用于 --patch-module 选项的形式。

模块模式形式

模块模式形式允许以规则方式组织的任意数量的模块的模块源路径的简明规范。

该模式由以下规则定义,这些规则按顺序应用:

修补模块

javac 允许使用 --patch-module 选项将任何内容(无论是源代码还是编译形式)修补到任何模块中。您可能希望这样做以将要在运行时修补的类的替代实现编译到 JVM 中,或者将其他类注入到模块中,例如在测试时。

选项的形式是:

路径分隔符在 Windows 上是 ;,否则是 :。为模块指定的路径必须指定模块内容的包层次结构的根

对于任何给定的模块,该选项最多可以给出一次。路径上的任何内容都将隐藏路径中和修补模块中任何类似名称的内容。

将源代码修补到多个模块时,还必须使用 --module-source-path ,以便输出目录按模块层次结构组织,并能够保存正在编译的模块的已编译类文件。

注释处理

javac 命令为注释处理提供直接支持。

注解处理器的 API 在 javax.annotation.processingjavax.lang.model 包和子包中定义。

注释处理的工作原理

除非使用 -proc:none 选项禁用注释处理,否则编译器会搜索任何可用的注解处理器。可以使用-processorpath 选项指定搜索路径。如果未指定路径,则使用用户类路径。处理器通过名为 META-INF/services/javax.annotation.processing 的服务提供商配置文件定位。搜索路径上的处理器。此类文件应包含要使用的任何注解处理器的名称,每行列出一个。或者,可以使用 -processor 选项明确指定处理器。

在命令行上扫描源文件和类以确定存在哪些注释后,编译器查询处理器以确定它们处理哪些注解。找到匹配项时,将调用处理器。处理器可以声明它处理的注释,在这种情况下,不会进一步尝试为这些注释找到任何处理器。声明所有注释后,编译器不会搜索其他处理器。

如果任何处理器生成新的源文件,则会进行另一轮注释处理:扫描任何新生成的源文件,并像以前一样处理注解。在前几轮调用的任何处理器也会在所有后续轮次中调用。这一直持续到没有新的源文件生成为止。

在没有生成新源文件的一轮发生后,最后一次调用注解处理器,让它们有机会完成任何剩余的工作。最后,除非使用 -proc:only 选项,否则编译器编译原始和所有生成的源文件。

如果您使用注解处理器生成要包含在编译中的其他源文件,则可以指定一个默认模块用于新生成的文件,以便在未生成模块声明时使用。在这种情况下,请使用 --default-module-for-created-files 选项。

编译环境和运行时环境。

源文件和先前编译的类文件中的声明由 compilation environment 中的 javac 分析,该 compilation environment 不同于用于执行 javac 本身的 runtime environment。尽管许多 javac 选项和 Java 发射器 的类似命名选项之间存在故意的相似性,例如 --class-path--module-path 等,但重要的是要理解,通常 javac 选项只会影响源文件所在的环境已编译,不影响javac本身的运行。

在使用注解处理器时,编译环境和运行时环境之间的区别很重要。尽管注解处理器处理存在于编译环境中的元素(声明),但注解处理器本身是在运行时环境中执行的。如果注解处理器依赖于不在模块中的库,则可以将这些库与注解处理器本身一起放置在处理器路径上。 (请参阅 --processor-path 选项。)如果注解处理器及其依赖项位于模块中,则应改用处理器模块路径。 (请参阅 --processor-module-path 选项。)当这些不足时,可能需要提供运行时环境的进一步配置。这可以通过两种方式完成:

  1. 如果从命令行调用 javac,则可以通过在选项前加上 -J 来将选项传递给底层运行时。

  2. 您可以直接启动 Java 虚拟机的实例,并使用命令行选项和 API 来配置一个环境,在该环境中,可以通过其中一个 APIs 调用 javac

为平台的早期版本编译

javac 可以使用 --release 选项或 --source /-source--target /-target 选项以及用于指定平台类的附加选项来编译要在其他版本平台上使用的代码。

根据所需的平台版本,对某些可以使用的选项有一些限制。

使用 --release 选项时,只能使用该版本支持的文档化 API;您不能使用任何选项来破坏封装以访问任何内部类。

APIs

可以通过三种不同的方式使用 API 调用 javac 编译器:

Java 编译器 API
这提供了调用编译器的最灵活方式,包括编译内存缓冲区或其他非标准文件系统中提供的源文件的能力。
工具提供者API

javacToolProvider 可以通过调用 ToolProvider.findFirst("javac") 获得。这将返回一个具有命令行工具等效功能的对象。

Note:此 API 不应与 javax.tools 包中的同名 API 混淆。

javac 遗留 API
保留此 API 只是为了向后兼容。所有新代码都应该使用 Java Compiler API 或 ToolProvider API。

Note: 在名称以 com.sun.tools.javac 开头的包(com.sun.tools.javac 的子包)中找到的所有其他类和方法都是严格内部的,随时可能更改。

使用示例 - Xlint键

cast

警告不必要和多余的转换,例如:

String s = (String) "Hello!"

classfile
警告与类文件内容相关的问题。
deprecation

警告有关已弃用项目的使用。例如:

java.util.Date myDate = new java.util.Date();
int currentDay = myDate.getDay(); 

自 JDK 1.1 以来,java.util.Date.getDay 方法已被弃用。

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 块中的案例,而不是块中的最后一个案例,其代码不包含 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 选项,则编译器会发出有关可能掉入案例的警告,以及相关案例的行号。

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。当 try 块退出时执行 finally 块。在此示例中,当控制权转移到 catch 块时,int 方法退出。但是,finally 块必须执行,所以它会执行,即使控制权已转移到方法之外。

options
警告与使用命令行选项相关的问题。参见 为平台的早期版本编译
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 '...' 

当编译器遇到 varargs 方法时,它会将 varargs 形式参数转换为数组。在方法 ClassWithVarargsMethod.varargsMethod 中,编译器将 varargs 形式参数 String... s 转换为形式参数 String[] s,这是一个与方法 ClassWithOverridingMethod.varargsMethod 的形式参数相匹配的数组。因此,此示例可以编译。

path

警告命令行上的无效路径元素和不存在的路径目录(关于类路径、源路径和其他路径)。无法使用 @SuppressWarnings 注释抑制此类警告。例如:

  • Linux and macOS: javac -Xlint:path -classpath /nonexistentpath Example.java

  • Windows: javac -Xlint:path -classpath C:\nonexistentpath Example.java

processing

警告与注释处理相关的问题。当您有一个带有注解的类,并且您使用的注解处理器无法处理该类型的注解时,编译器会生成此警告。例如,下面是一个简单的注解处理器:

Source file AnnoProc.java :

import java.util.*;
import javax.annotation.processing.*;
import javax.lang.model.*;
import javax.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();
  }
} 

Source file AnnosWithoutProcessors.java :

@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 

要解决此问题,您可以调用 static 方法 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> 分别变为 ArrayListList

ls 命令具有参数化类型 List<String>。当 l 引用的 List 分配给 ls 时,编译器会生成未检查警告。在编译时,编译器和 JVM 无法确定 l 是否引用了 List<String> 类型。在这种情况下,l 不指代 List<String> 类型。结果,堆污染发生了。

当静态类型为 List<Number>List 对象 l 分配给另一个具有不同静态类型 List<String>List 对象 ls 时,会发生堆污染情况。但是,编译器仍然允许这种赋值。它必须允许此分配以保持与不支持泛型的 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 

当编译器遇到可变参数方法时,它会将 varargs 形式参数转换为数组。但是,Java 编程语言不允许创建参数化类型的数组。在方法 ArrayBuilder.addToList 中,编译器将 varargs 形式参数 T... 元素转换为形式参数 T[] 元素,一个数组。但是,由于类型擦除,编译器将 varargs 形式参数转换为 Object[] 元素。因此,存在堆污染的可能性。