名称
javac - 读取Java声明并将其编译为类文件
概要
javac
[选项] [源文件或类名]
- 选项
- 命令行选项。
- 源文件或类名
-
要编译的源文件(例如,
Shape.java
)或要处理注解的先前编译类的名称(例如,geometry.MyShape
)。
描述
javac命令读取用Java编程语言编写的包含模块、包和类型声明的源文件,并将它们编译为在Java虚拟机上运行的类文件。
javac命令还可以处理Java源文件和类中的注解。
源文件必须具有.java
文件扩展名。类文件具有.class
文件扩展名。源文件和类文件通常具有标识内容的文件名。例如,名为Shape
的类将在名为Shape.java
的源文件中声明,并编译为名为Shape.class
的类文件。
有两种指定源文件给javac
的方法:
-
对于少量源文件,可以在命令行上列出它们的文件名。
-
对于大量源文件,可以使用命令行上的
@
文件名选项指定一个列出它们文件名的参数文件。有关该选项的描述,请参见标准选项,以及命令行参数文件中对javac
参数文件的描述。
在命令行或参数文件中指定的源文件的顺序并不重要。javac
将这些文件一起编译为一个组,并将自动解析各个源文件中声明之间的任何依赖关系。
javac
期望源文件在文件系统上的一个或多个目录层次结构中排列,详见源代码的排列。
要编译源文件,javac
需要找到源文件中的代码使用、扩展或实现的每个类或接口的声明。这使得javac
可以检查代码是否有权访问这些类和接口。您可以使用命令行选项告诉javac
在哪里搜索这些类和接口的源文件,而不是显式指定这些类和接口的源文件。如果您之前已经编译了这些源文件,您可以使用选项告诉javac
在哪里搜索相应的类文件。所有以"path"结尾的选项在标准选项中有描述,并在配置编译和搜索模块、包和类型声明中进一步描述。
默认情况下,javac
将每个源文件编译为与源文件相同目录中的类文件。但是,建议使用-d
选项指定一个单独的目标目录。
- 编译代码以在JDK早期版本上运行。
- 编译代码以在调试器下运行。
- 检查Java源代码中的风格问题。
- 检查
javadoc
注释(/** ... */
)中的问题。 - 处理源文件和类文件中的注解。
- 升级和修补编译时环境中的模块。
javac
支持为平台早期版本编译,也可以通过多种API之一从Java代码中调用。
选项
某些选项接受一个或多个参数。如果参数包含空格或其他空白字符,则根据调用javac的环境的约定对值进行引用。如果选项以单破折号(-
)开头,则参数应直接跟在选项名称后面,或者应使用冒号(:
)或空格进行分隔,具体取决于选项。如果选项以双破折号(--
)开头,则参数可以通过空格或等号(=
)字符进行分隔,而不需要额外的空格。例如,
-Aname="J. Duke"
-proc:only
-d myDirectory
--module-version 3
--module-version=3
在以下选项列表中,path的参数表示搜索路径,由平台路径分隔符字符(在Windows上为分号;
,在其他系统上为冒号:
)分隔的文件系统位置列表组成。根据选项的不同,文件系统位置可以是目录、JAR文件或JMOD文件。
标准选项
-
@
文件名 -
从文件中读取选项和文件名。为了缩短或简化
javac
命令,您可以指定一个或多个包含javac
命令参数(除了-J
选项)的文件。这样可以在任何操作系统上创建任意长度的javac
命令。请参阅命令行参数文件。 -
-A
键[=
值] -
指定要传递给注解处理器的选项。这些选项不会被
javac
直接解释,但可以供各个处理器使用。键值应该是一个或多个由点(.
)分隔的标识符。 -
--add-modules
模块,
模块 -
指定要解析的根模块,以及初始模块之外的模块,或者如果模块是
ALL-MODULE-PATH
,则是模块路径上的所有模块。 -
--boot-class-path
路径 或-bootclasspath
路径 -
覆盖引导类文件的位置。
注意:只能在编译JDK 9之前的版本时使用。如适用,请参阅
--release
、-source
或-target
中的描述。对于JDK 9或更高版本,请参阅--system
。 -
--class-path
路径,-classpath
路径,或-cp
路径 -
指定查找用户类文件和注解处理器的位置。此类路径会覆盖
CLASSPATH
环境变量中的用户类路径。-
如果未指定
--class-path
,-classpath
或-cp
,则用户类路径是CLASSPATH
环境变量的值(如果设置了),否则是当前目录。 -
如果不是为模块编译代码,且未指定
--source-path
或-sourcepath`选项,则还会在用户类路径中搜索源文件。 -
如果未指定
-processorpath
选项,则还会在类路径中搜索注解处理器。
-
-
-d
目录 -
设置目标目录(或类输出目录)以存放类文件。如果类属于包的一部分,则
javac
会将类文件放在反映模块名称(如果适用)和包名称的子目录中。如果这些目录不存在,将会创建它们。如果未指定
-d
选项,则javac
会将每个类文件放在生成它的源文件所在的相同目录中。除非为多个模块编译代码,否则类输出目录的内容将以包层次结构组织。当为多个模块编译代码时,输出目录的内容将以模块层次结构组织,每个模块的内容都在单独的子目录中,每个子目录都以包层次结构组织。
注意:当为一个或多个模块编译代码时,将自动检查类输出目录以搜索先前编译的类。当不为模块编译代码时,为了向后兼容,目录不会自动检查以前编译的类,因此建议将类输出目录指定为用户类路径上的一个位置,使用
--class-path
选项或其替代形式之一。 -
-deprecation
-
显示每个使用或覆盖已弃用成员或类的描述。如果没有
-deprecation
选项,javac
会显示使用或覆盖已弃用成员或类的源文件的摘要。-deprecation
选项是-Xlint:deprecation
的简写。 -
--enable-preview
-
启用预览语言特性。与
-source
或--release
一起使用。 -
-encoding
编码 -
指定源文件使用的字符编码,例如EUC-JP和UTF-8。如果未指定
-encoding
选项,则使用平台默认转换器。 -
-endorseddirs
目录 -
覆盖认可标准路径的位置。
注意:只能在编译JDK 9之前的版本时使用。如适用,请参阅
--release
、-source
或-target
中的描述。 -
-extdirs
目录 -
覆盖已安装扩展的位置。
目录
是由平台路径分隔符(在Windows上为;
,否则为:
)分隔的目录列表。在指定的目录中搜索每个JAR文件的类文件。找到的所有JAR文件都将成为类路径的一部分。如果您正在为支持扩展机制的平台版本编译,则此选项指定包含扩展类的目录。请参阅[为平台的其他版本编译]。
注意:只能在编译JDK 9之前的版本时使用。如适用,请参阅
--release
、-source
或-target
中的描述。 -
-g
- 生成所有调试信息,包括局部变量。默认情况下,只生成行号和源文件信息。
-
-g:
[lines
,vars
,source
] -
仅生成由逗号分隔的关键字列表指定的调试信息类型。有效的关键字包括:
-
lines
- 行号调试信息。
-
vars
- 局部变量调试信息。
-
source
- 源文件调试信息。
-
-
-g:none
- 不生成调试信息。
-
-h
目录 -
指定生成的本机头文件放置的位置。
当您指定此选项时,为包含本地方法或具有一个或多个常量被注解为
java.lang.annotation.Native
的类生成本机头文件。如果类属于包的一部分,则编译器会将本机头文件放在反映模块名称(如果适用)和包名称的子目录中。如果这些目录不存在,将会创建它们。 -
--help
,-help
或-?
- 打印标准选项的概要。
-
--help-extra
或-X
- 打印一组额外选项的概要。
-
--help-lint
-
打印
-Xlint
选项支持的键。 -
-implicit:
[none
,class
] -
指定是否为隐式引用的文件生成类文件:
-
-implicit:class
--- 自动生成类文件。 -
-implicit:none
--- 禁止生成类文件。
如果未指定此选项,则默认情况下会自动生成类文件。在这种情况下,如果同时进行注解处理生成了任何类文件,则编译器会发出警告。当
-implicit
选项被明确设置时,不会发出警告。请参阅搜索模块、包和类型声明。 -
-
-J
选项 -
将选项传递给运行时系统,其中选项是在java命令中描述的Java选项之一。例如,
-J-Xms48m
将启动内存设置为48 MB。注意:
CLASSPATH
环境变量、-classpath
选项、-bootclasspath
选项和-extdirs
选项不指定用于运行javac
的类。尝试使用这些选项和变量自定义编译器实现是有风险的,通常不会达到您想要的效果。如果必须自定义编译器实现,则使用-J
选项将选项传递给底层Java启动器。 -
--limit-modules
模块,
模块* - 限制可观察模块的范围。
-
--module
模块名(,
模块名*)或-m
模块名(,
模块名*) - 编译那些比输出目录中对应文件更新的命名模块中的源文件。
-
--module-path
路径 或-p
路径 - 指定应用程序模块的位置。
-
--module-source-path
模块源路径 - 指定在编译多个模块中的代码时要查找源文件的位置。请参阅[编译模式]和模块源路径选项。
-
--module-version
版本 - 指定正在编译的模块的版本。
-
-nowarn
-
禁用警告消息。此选项的操作与
-Xlint:none
选项相同。 -
-parameters
-
为方法参数的反射生成元数据。将构造函数和方法的形式参数名称存储在生成的类文件中,以便Reflection API中的方法
java.lang.reflect.Executable.getParameters
可以检索它们。 -
-proc:
[none
,only
,full
] -
控制是否进行注解处理和编译。
-proc:none
表示编译将在没有注解处理的情况下进行。-proc:only
表示仅进行注解处理,没有后续编译。 如果未使用此选项,或指定了-proc:full
,则会进行注解处理和编译。 -
-processor
class1[,
class2,
class3...] - 要运行的注解处理器的名称。 这将绕过默认的发现过程。
-
--processor-module-path
path - 指定用于查找注解处理器的模块路径。
-
--processor-path
path 或-processorpath
path - 指定注解处理器的位置。 如果未使用此选项,则将在类路径中搜索处理器。
-
-profile
profile -
检查所使用的 API 是否在指定的配置文件中可用。 此选项已弃用,可能会在将来的版本中删除。
注意: 只能在编译 JDK 9 之前的版本时使用。 如适用,请参阅
--release
、-source
或-target
中的描述以获取详细信息。 -
--release
release -
根据指定的 Java SE 发行版的规则编译源代码,生成针对该发行版的类文件。 源代码针对指定发行版的 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 如该发行版中定义的那样。
注意: 使用
--release
时,不能同时使用--source
/-source
或--target
/-target
选项。注意: 使用
--release
指定支持 Java 平台模块系统的发行版时,不能使用--add-exports
选项来扩展指定发行版中由 Java SE、JDK 和标准模块导出的包集。 -
-s
directory -
指定用于放置生成的源文件的目录。 如果类是包的一部分,则编译器将源文件放在反映模块名称(如果适用)和包名称的子目录中。 如果这些目录或必要的子目录不存在,将会创建它们。
除非编译多个模块的代码,否则源输出目录的内容将以包层次结构组织。 当编译多个模块的代码时,源输出目录的内容将以模块层次结构组织,每个模块的内容都在单独的子目录中,每个子目录都以包层次结构组织。
-
--source
release 或-source
release -
根据指定的 Java SE 发行版的规则编译源代码。 release 的支持值是当前的 Java SE 发行版和有限数量的先前发行版,详细信息请参阅命令行帮助。
如果未指定此选项,则默认情况下将根据当前 Java SE 发行版的规则编译源代码。
-
--source-path
path 或-sourcepath
path -
指定要查找源文件的位置。 除非一起编译多个模块,否则这是用于搜索类或接口定义的源代码路径。
注意: 通过类路径找到的类在找到其源文件时可能会重新编译。 请参阅 搜索模块、包和类型声明。
-
--system
jdk |none
- 覆盖系统模块的位置。
-
--target
release 或-target
release -
生成适用于指定 Java SE 发行版的
class
文件。 release 的支持值是当前的 Java SE 发行版和有限数量的先前发行版,详细信息请参阅命令行帮助。注意: 目标发行版必须等于或高于源发行版。 (请参阅
--source
。) -
--upgrade-module-path
path - 覆盖可升级模块的位置。
-
-verbose
- 输出有关编译器正在执行的操作的消息。 消息包括有关每个加载的类和每个编译的源文件的信息。
-
--version
或-version
- 打印版本信息。
-
-Werror
- 在出现警告时终止编译。
额外选项
-
--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 -
覆盖认可标准路径的位置。
注意:仅在编译 JDK 9 之前的版本时才能使用。如适用,请参阅
--release
、-source
或-target
中的描述以获取详细信息。 -
-Djava.ext.dirs=
dirs -
覆盖已安装扩展的位置。
注意:仅在编译 JDK 9 之前的版本时才能使用。如适用,请参阅
--release
、-source
或-target
中的描述以获取详细信息。 -
--patch-module
module=
path - 覆盖或增补 JAR 文件或目录中的模块的类和资源。
-
-Xbootclasspath:
path -
覆盖引导类文件的位置。
注意:仅在编译 JDK 9 之前的版本时才能使用。如适用,请参阅
--release
、-source
或-target
中的描述以获取详细信息。 -
-Xbootclasspath/a:
path -
向引导类路径添加后缀。
注意:仅在编译 JDK 9 之前的版本时才能使用。如适用,请参阅
--release
、-source
或-target
中的描述以获取详细信息。 -
-Xbootclasspath/p:
path -
向引导类路径添加前缀。
注意:仅在编译 JDK 9 之前的版本时才能使用。如适用,请参阅
--release
、-source
或-target
中的描述以获取详细信息。 -
-Xdiags:
[compact
,verbose
] - 选择诊断模式。
-
-Xdoclint
- 启用对文档注释中问题的推荐检查。
-
-Xdoclint:
(all
|none
|[-
]group)[/
access] -
启用或禁用文档注释中特定组的检查。
group 可以具有以下值之一:
accessibility
、html
、missing
、reference
、syntax
。变量 access 指定
-Xdoclint
选项检查的类和成员的最小可见级别。它可以具有以下值(从最可见到最不可见的顺序):public
、protected
、package
、private
。默认的 access 级别是
private
。当以
doclint:
为前缀时,group 名称和all
可以与@SuppressWarnings
一起使用,以抑制有关正在编译的代码部分中文档注释的警告。有关这些检查组的更多信息,请参阅
javadoc
命令文档的 DocLint 部分。在javac
命令中,默认情况下禁用-Xdoclint
选项。例如,以下选项检查具有受保护及更高访问级别(包括受保护和公共)的类和成员(具有所有检查组):
-Xdoclint:all/protected
以下选项启用所有访问级别的所有检查组,但不会检查具有包及更高访问级别(包括包、受保护和公共)的类和成员的 HTML 错误:
-Xdoclint:all,-html/package
-
-Xdoclint/package:
[-
]packages(,
[-
]package)* -
启用或禁用特定包中的检查。每个 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 语句的一个 case 掉到下一个 case。 -
finally
:警告finally
子句不正常终止。 -
lossy-conversions
:警告复合赋值中可能的损失转换。 -
missing-explicit-ctor
:警告在导出包中的公共和受保护类中缺少显式构造函数。 -
module
:警告与模块系统相关的问题。 -
opens
:警告与模块 opens 相关的问题。 -
options
:警告与命令行选项使用相关的问题。 -
output-file-clash
:警告如果在编译期间覆盖任何输出文件。例如,在不区分大小写的文件系统上可能会发生这种情况。 -
overloads
:警告与方法重载相关的问题。 -
overrides
:警告与方法覆盖相关的问题。 -
path
:警告命令行上的无效路径元素。 -
preview
:警告使用预览语言特性。 -
processing
:警告与注解处理相关的问题。 -
rawtypes
:警告使用原始类型。 -
removal
:警告使用已标记为移除的 API。 -
requires-automatic
:警告开发人员在 requires 子句中使用自动模块。 -
requires-transitive-automatic
:警告关于 requires transitive 中的自动模块。 -
serial
:警告不提供序列化版本 ID 的可序列化类。还警告从可序列化元素访问非公共成员。 -
static
:警告使用实例访问静态成员。 -
strictfp
:警告不必要使用strictfp
修饰符。 -
synchronization
:警告在值类实例上尝试同步。 -
text-blocks
:警告文本块缩进中不一致的空格字符。 -
this-escape
:警告构造函数在子类初始化之前泄漏this
。 -
try
:警告与 try 块的使用相关的问题(即,try-with-resources)。 -
unchecked
:警告有关未经检查的操作。 -
varargs
:警告关于潜在不安全的vararg
方法。 -
none
:禁用所有警告。
除了
all
和none
之外,键可以与@SuppressWarnings
注释一起在要编译的源代码部分中抑制警告。请参阅 使用 -Xlint 键的示例。
-
-
-Xmaxerrs
number - 设置要打印的最大错误数。
-
-Xmaxwarns
number - 设置要打印的最大警告数。
-
-Xpkginfo:
[always
,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
文件。注意:如果
package-info.java
文件中的所有注解都具有RetentionPolicy.SOURCE
,则可能会生成一个空的package-info.class
文件。 -
nonempty
-
仅当
package-info.java
包含具有RetentionPolicy.CLASS
或RetentionPolicy.RUNTIME
的注解时才生成package-info.class
文件。
-
-
-Xplugin:
name args - 指定要运行的插件的名称和可选参数。如果提供了 args,则应该对 name 和 args 进行引用或以其他方式转义名称和所有参数之间的空格字符。有关插件的 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
环境变量的值(如果已设置)。但是,建议不设置此环境变量,并在需要时使用--class-path
选项为类路径提供显式值。
JDK_JAVAC_OPTIONS
JDK_JAVAC_OPTIONS
环境变量的内容,由空格( )或空白字符(\n
、\t
、\r
或\f
)分隔,作为传递给javac
的命令行参数列表的前缀。
环境变量的编码要求与系统上javac
命令行相同。JDK_JAVAC_OPTIONS
环境变量内容的处理方式与命令行中指定的方式相同。
单引号('
)或双引号("
)可用于括起包含空格字符的参数。在开放引号和第一个匹配的闭合引号之间的所有内容将通过简单删除引号对来保留。如果找不到匹配的引号,则启动器将中止并显示错误消息。支持@
文件,因为它们在命令行中指定。但是,与@
文件一样,不支持使用通配符。
引用包含空格的参数的示例:
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的示例@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
创建名为
sources
的文件,其中包含以下内容:MyClass1.java MyClass2.java MyClass3.java
然后,运行以下
javac
命令:javac @options @sources
- 带路径的参数文件
-
参数文件可以具有路径,但文件内部的任何文件名都是相对于当前工作目录而不是
path1
或path2
:javac @path1/options @path2/sources
源代码的排列
在Java语言中,类和接口可以组织到包中,包可以组织到模块中。javac
期望文件系统目录中源文件的物理排列将反映类组织到包中,包组织到模块中的结构。
通常采用的约定是模块名称和包名称以小写字母开头,类名以大写字母开头。
包的源代码排列
当类和接口组织到一个包中时,该包表示为一个目录,任何子包表示为子目录。
例如:
-
包
p
表示为名为p
的目录。 -
包
p.q
-- 即包p
的子包q
-- 表示为目录p
的子目录q
。表示包p.q
的目录树因此在Windows上为p\q
,在其他系统上为p/q
。 -
包
p.q.r
表示为目录树p\q\r
(在Windows上)或p/q/r
(在其他系统上)。
在目录或子目录中,.java
文件表示相应包或子包中的类和接口。
例如:
-
在包
p
中声明的类X
由p
目录中的文件X.java
表示。 -
在包
p.q
中声明的类Y
由p
目录的q
子目录中的文件Y.java
表示。 -
在包
p.q.r
中声明的类Z
由p\q
(在Windows上)或p/q
(在其他系统上)中的Z.java
文件表示。
在某些情况下,将代码拆分为按上述方式结构化的单独目录,并将目录列表指定给javac
是方便的。
模块的源代码排列
在Java语言中,模块是为重用而设计的一组包。除了用于类和接口的.java
文件外,每个模块还有一个名为module-info.java
的源文件,其中:
-
声明模块的名称;
-
列出模块导出的包(以允许其他模块重用);
-
列出模块所需的其他模块(以重用其导出的包)。
当包组织到一个模块中时,模块由表示模块中包的一个或多个目录表示,其中一个目录包含module-info.java
文件。可能方便但不是必需的,使用一个单独的目录,以模块命名,包含module-info.java
文件以及表示模块中包的目录树(即上述的包层次结构)。模块的源代码排列方式通常由开发环境(IDE)或构建系统采用的约定决定。
例如:
-
模块
a.b.c
可以由所有系统中的目录a.b.c
表示。 -
模块的声明由
a.b.c
目录中的module-info.java
文件表示。 -
如果模块包含包
p.q.r
,则a.b.c
目录包含p\q\r
(在Windows上)或p/q/r
(在其他系统上)的目录树。
开发环境可能规定模块命名的目录层次结构与javac
要读取的源文件之间的某种目录层次结构。
例如:
-
模块
a.b.c
可以由目录a.b.c
表示 -
模块的声明和模块的包可以位于
a.b.c
的某个子目录中,例如src\main\java
(在Windows上)或src/main/java
(在其他系统上)。
配置编译
本节描述如何配置javac
执行基本编译。
有关在为支持模块的平台版本编译时使用的其他详细信息,请参阅配置模块系统。
源文件
- 在命令行上指定要编译的源文件。
如果没有编译错误,则相应的类文件将放在输出目录中。
某些系统可能限制可以放在命令行上的内容量;为了解决这些限制,可以使用参数文件。
在为模块编译代码时,还可以间接指定源文件,通过使用--module
或-m
选项。
输出目录
- 使用
-d
选项指定一个输出目录,用于放置编译后的类文件。
通常会按照包层次结构
组织,除非您正在从多个模块编译源代码,此时将按照模块层次结构
组织。
编译完成后,如果正在编译一个或多个模块,则可以将输出目录放在Java启动器的模块路径上;否则,可以将输出目录放在Java启动器的类路径上。
预编译代码
要编译的代码可能引用平台提供的库之外的库。如果是这样,必须将这些库放在类路径或模块路径上。如果库代码不在模块中,请将其放在类路径上;如果在模块中,请将其放在模块路径上。
-
使用
--class-path
选项指定要放置在类路径上的库。类路径上的位置应该按照包层次结构组织。您还可以使用选项的其他形式:-classpath
或-cp
。 -
使用
--module-path
选项指定要放置在模块路径上的库。模块路径上的位置应该是模块或模块目录。您还可以使用选项的另一种形式:-p
。有关如何修改库模块的默认配置的详细信息,请参见配置模块系统。
注意:类路径和模块路径的选项并不是互斥的,尽管在为一个或多个模块编译代码时指定类路径并不常见。
额外的源文件
要编译的代码可能引用未在命令行上指定的其他源文件中的类型。如果是这样,您必须将这些源文件放在源路径或模块路径上。您只能指定这些选项中的一个:如果您不是为模块编译代码,或者只编译单个模块的代码,请使用源路径;如果您正在为多个模块编译代码,请使用模块源路径。
-
使用
--source-path
选项指定可能被javac读取的其他源文件的位置。源路径上的位置应该按照包层次结构组织。您还可以使用选项的另一种形式:-sourcepath
。 -
使用
--module-source-path
选项一次或多次指定不同模块中的其他源文件的位置,这些源文件可能被javac读取,或者在编译多个模块的源文件时。您可以为每个模块单独指定位置,或者组织源文件以便可以一起指定位置。有关更多详细信息,请参见模块源路径选项。
如果您想要引用其他源文件中的类型但不希望对其进行编译,请使用-implicit
选项。
注意:如果您正在为多个模块编译代码,您必须始终指定一个模块源路径,并且命令行上指定的所有源文件必须位于模块源路径的一个目录中,或者其子目录中。
编译多个源文件的示例
此示例编译Aloha.java
、GutenTag.java
、Hello.java
和Hi.java
源文件,它们位于greetings
包中。
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
如果您将greetings.Hi
更改为使用横幅实用程序,则该实用程序也需要通过用户类路径访问。
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
配置模块系统
如果要在编译中包含额外的模块,请使用--add-modules
选项。当您编译不在模块中的代码,或者在自动模块中的代码引用额外模块的API时,可能需要这样做。
如果要限制编译中的模块集,请使用--limit-modules
选项。如果要确保您正在编译的代码能够在安装了有限模块集的系统上运行,这可能很有用。
如果要打破封装并指定额外的包应被视为从模块导出,请使用--add-exports
选项。在执行白盒测试时可能会有用;在生产代码中依赖内部API访问是强烈不建议的。
如果要指定额外的包应被视为模块所需,请使用--add-reads
选项。在执行白盒测试时可能会有用;在生产代码中依赖内部API访问是强烈不建议的。
您可以使用--patch-module
选项将额外内容补丁到任何模块中。有关更多详细信息,请参见[补丁模块]。
搜索模块、包和类型声明
要编译源文件,编译器通常需要有关模块或类型的信息,但声明不在命令行上指定的源文件中。
javac
需要每个在源文件中使用、扩展或实现的类或接口的类型信息。这包括源文件中未明确提及但通过继承提供信息的类和接口。
例如,当您创建java.awt.Window
的子类时,您还在使用Window
的祖先类:java.awt.Container
、java.awt.Component
和java.lang.Object
。
在为模块编译代码时,编译器还需要有该模块的声明。
成功的搜索可能会产生一个类文件、一个源文件或两者。如果找到并使用了两者,则可以使用-Xprefer
选项指示编译器使用哪一个。
如果搜索找到并使用了一个源文件,则默认情况下javac
会编译该源文件。可以使用-implicit
更改此行为。
直到注解处理完成后,编译器可能不会发现对某些类型信息的需求。当在源文件中找到类型信息且未指定-implicit
选项时,编译器会发出警告,指出正在编译该文件而不受注解处理的影响。要禁用警告,要么在命令行上指定文件(以便它将受到注解处理的影响),要么使用-implicit
选项指定是否应为这种源文件生成类文件。
javac
定位这些类型的声明的方式取决于引用是在模块代码内还是在模块外。
搜索面向包的路径
在搜索由包定位位置组成的路径上的源文件或类文件时,javac
将依次检查路径上的每个位置,以查看可能存在文件的情况。特定文件的第一次出现会遮蔽(隐藏)后续出现的同名文件。这种遮蔽不会影响对具有不同名称的文件的搜索。当搜索源文件时,这可能很方便,因为源文件可能分组在不同位置,例如共享代码、特定于平台的代码和生成的代码。当将类文件的替代版本注入到包中时,这也可能很有用,用于调试或其他仪器化目的。但是,这也可能很危险,例如将不兼容的不同版本的库放在类路径上。
搜索面向模块的路径
在扫描任何模块路径以查找任何包或类型声明之前,javac
将懒惰地扫描以下路径和位置,以确定将在编译中使用的模块。
- 模块源路径(参见
--module-source-path
选项) - 可升级模块的路径(参见
--upgrade-module-path
选项) - 系统模块(参见
--system
选项) - 用户模块路径(参见
--module-path
选项)
对于任何模块,模块的第一次出现在扫描期间完全遮蔽(隐藏)任何后续出现的同名模块。在定位模块时,javac
能够确定模块导出的包,并为模块的内容关联每个模块一个面向包的路径。对于任何先前编译的模块,此路径通常是一个目录或提供内部类似目录层次结构的文件的单个条目,例如JAR文件。因此,当搜索在已知由模块导出的包中的类型时,javac
可以直接且高效地定位声明。
搜索模块声明
如果模块以前已经编译过,则模块声明位于包层次结构的根目录中名为module-info.class
的文件中。
如果模块是当前正在编译的模块之一,则模块声明将位于类输出目录中模块包层次结构的根目录中名为module-info.class
的文件中,或者位于源路径或模块源路径中的名为module-info.java
的文件中。
在引用不在模块中的类型时搜索声明
当在不在模块中的代码中搜索引用的类型时,javac
将在以下位置查找:
-
平台类(或平台模块的导出包中的类型)(仅适用于已编译的类文件)。
-
模块路径上任何模块的导出包中的类型(如果适用)(仅适用于已编译的类文件)。
-
类路径和/或源路径中的包中的类型:
-
如果两者都指定了,则
javac
将在类路径上查找已编译的类文件,并在源路径上查找源文件。 -
如果指定了类路径,但没有指定源路径,则
javac
将在类路径上查找已编译的类文件和源文件。 -
如果未指定类路径,则默认为当前目录。
-
在查找类路径和/或源路径上的类型时,如果找到了已编译的类文件和源文件,则默认情况下将使用最近修改的文件。如果源文件更新,则将对其进行编译,并可能覆盖文件的先前编译版本。您可以使用-Xprefer
选项来覆盖默认行为。
在模块中引用类型的声明
当在模块中的代码中搜索引用的类型时,javac
将检查封闭模块的声明,以确定类型是否在从另一个模块导出且可被封闭模块读取的包中。如果是这样,javac
将直接转到该模块的定义以找到所需类型的定义。除非模块是另一个正在编译的模块,否则javac
只会查找已编译的类文件。换句话说,javac
不会在平台模块或模块路径上查找源文件。
如果引用的类型不在其他可读模块中,javac
将检查正在编译的模块以尝试找到类型的声明。javac
将按以下方式查找类型的声明:
-
在命令行或源路径或模块源路径上指定的源文件。
-
输出目录中以前编译的文件。
目录层次结构
javac
通常假定源文件和已编译的类文件将组织在文件系统目录层次结构中,或者在支持内部目录层次结构的文件类型中,例如JAR文件。支持三种不同类型的层次结构:包层次结构、模块层次结构和模块源层次结构。
虽然javac
对源代码的组织相对宽松,除了期望源代码将组织在一个或多个包层次结构中之外,并且通常可以适应由开发环境和构建工具规定的组织,但Java工具总体上,特别是javac
和Java启动器,对已编译的类文件的组织更为严格,并将根据需要组织在包层次结构或模块层次结构中。
这些层次结构的位置通过命令行选项指定给javac
,其名称通常以"path"结尾,如--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-source-path
选项的实例,每个选项使用模块模式形式或模块特定形式,但受以下限制:
- 模块模式形式最多只能使用一次
- 对于任何给定模块,模块特定形式最多只能使用一次
如果对于任何模块使用了模块特定形式,则关联的搜索路径将覆盖可能从模块模式形式中推断出的任何路径。
模块特定形式
模块特定形式允许为任何特定模块给出显式搜索路径。该形式为:
--module-source-path
module-name=
file-path (path-separator file-path)*
路径分隔符字符在Windows上为;
,其他情况下为:
。
注意:这类似于--patch-module
选项使用的形式。
模块模式形式
模块模式形式允许对以规则方式组织的任意数量模块的模块源路径进行简洁指定。
--module-source-path
pattern
模式由以下规则定义,按顺序应用:
-
参数被视为由路径分隔符字符(在Windows上为
;
,其他情况下为:
)分隔的一系列段。 -
包含大括号的段形式为
string1{alt1 ( ,alt2 )* } string2
被认为是通过“展开”大括号而形成的一系列段:
string1 alt1 string2 string1 alt2 string2 依此类推...
大括号可以嵌套。
对于所有这样的大括号用法,都应用此规则。
-
每个段最多只能有一个星号(
*
)。如果段不包含星号,则被视为追加文件分隔符字符和星号。对于任何模块M,该模块的源路径是从通过将模块名称M替换为每个段中的星号而获得的一系列段中形成的。
注意:在此上下文中,星号仅用作特殊标记,用于表示路径中的模块名称位置。不应将其与文件名通配符字符
*
混淆,后者在大多数操作系统上都可以找到。
修补模块
javac允许使用--patch-module
选项将任何内容(无论是源代码还是已编译形式)补丁到任何模块中。您可能希望这样做以编译类的替代实现,以在运行时补丁到JVM中,或者向模块中注入其他类,例如在测试时。
选项的形式为:
--patch-module
module-name=
file-path (path-separator file-path )*
路径分隔符字符在Windows上为;
,其他情况下为:
。为模块给出的路径必须指定包层次结构的根目录以包含模块的内容
对于任何给定模块,该选项最多可以使用一次。路径上的任何内容将隐藏路径后面和修补模块中的同名内容。
当将源代码补丁到多个模块时,还必须使用--module-source-path
,以便输出目录组织在模块层次结构中,并能够容纳正在编译的模块的已编译类文件。
注解处理
javac
命令直接支持注解处理。
注解处理器的API定义在javax.annotation.processing
和javax.lang.model
包及其子包中。
注解处理的工作原理
除非使用-proc:none
选项禁用注解处理,否则编译器会搜索所有可用的注解处理器。可以使用-processorpath
选项指定搜索路径。如果未指定路径,则使用用户类路径。处理器通过名为META-INF/services/javax.annotation.processing.Processor
的服务提供者配置文件在搜索路径上定位。这些文件应包含要使用的任何注解处理器的名称,每行列出一个。或者,可以显式指定处理器,使用-processor
选项。
在扫描命令行上的源文件和类以确定存在哪些注解后,编译器会查询处理器以确定它们处理哪些注解。找到匹配项后,将调用处理器。处理器可以声明它处理的注解,在这种情况下,不会进一步尝试查找任何处理这些注解的处理器。在声明所有注解之后,编译器不会再搜索其他处理器。
如果任何处理器生成新的源文件,则会发生另一轮注解处理:扫描任何新生成的源文件,并像以前一样处理注解。任何在之前轮次上调用的处理器也会在所有后续轮次上调用。这将持续到不再生成新的源文件为止。
在发生不再生成新源文件的一轮后,最后一次调用注解处理器,以便让它们有机会完成任何剩余工作。最后,除非使用-proc:only
选项,否则编译器会编译原始源文件和所有生成的源文件。
如果使用生成额外源文件以包含在编译中的注解处理器,可以指定用于新生成文件的默认模块,用于在没有生成模块声明的情况下使用。在这种情况下,使用--default-module-for-created-files
选项。
编译环境和运行时环境。
源文件和先前编译的类文件中的声明由javac
在与用于执行javac
的运行时环境不同的编译环境中进行分析。尽管许多javac
选项与Java 启动器的同名选项有意相似,例如--class-path
、--module-path
等,但重要的是要理解,一般来说,javac
选项只影响源文件编译的环境,并不影响javac
本身的操作。
在使用注解处理器时,编译环境和运行时环境之间的区别是重要的。尽管注解处理器处理存在于编译环境中的元素(声明),但注解处理器本身在运行时环境中执行。如果注解处理器依赖于不在模块中的库,则可以将库与注解处理器本身一起放在处理器路径上。 (请参阅--processor-path
选项。)如果注解处理器及其依赖项在模块中,则应改用处理器模块路径。 (请参阅--processor-module-path
选项。)当这些不足以时,可能需要进一步配置运行时环境。可以通过两种方式来完成:
-
如果从命令行调用
javac
,则可以通过在选项前加上-J
来将选项传递给底层运行时。 -
您可以直接启动Java虚拟机的实例,并使用命令行选项和API来配置一个环境,其中可以通过其APIs之一调用
javac
。
为平台的早期版本编译
javac
可以编译用于平台其他版本的代码,使用--release
选项,或--source
/-source
和--target
/-target
选项,以及其他选项来指定平台类。
根据所需的平台版本,对某些选项的使用有一些限制。
-
当为JDK 8及更早版本编译时,不能使用任何用于模块系统的选项。这包括以下所有选项:
--module-source-path
、--upgrade-module-path
、--system
、--module-path
、--add-modules
、--add-exports
、--add-opens
、--add-reads
、--limit-modules
、--patch-module
如果使用
--source
/-source
或--target
/-target
选项,则还应使用引导类路径系列选项设置适当的平台类。 -
当为JDK 9及更高版本编译时,不能使用任何用于配置引导类路径的选项。这包括以下所有选项:
-Xbootclasspath/p:
、-Xbootclasspath
、-Xbootclasspath/a:
、-endorseddirs
、-Djava.endorsed.dirs
、-extdirs
、-Djava.ext.dirs
、-profile
如果使用
--source
/-source
或--target
/-target
选项,则还应使用--system
选项设置适当安装的JDK版本的位置来提供适当的平台类。
使用--release
选项时,只能使用该版本的支持文档化API;不能使用任何选项来打破封装以访问任何内部类。
APIs
javac
编译器可以使用三种不同的方式调用API:
- Java编译器API
- 这提供了调用编译器的最灵活方式,包括能够编译提供在内存缓冲区或其他非标准文件系统中的源文件的能力。
- ToolProvider API
-
可以通过调用
ToolProvider.findFirst("javac")
来获取javac
的ToolProvider
。这将返回一个具有与命令行工具相同功能的对象。注意:此API不应与
javax.tools
包中的同名API混淆。 -
javac
Legacy API - 该API仅用于向后兼容。所有新代码应使用Java编译器API或ToolProvider API。
注意:在以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();
方法
java.util.Date.getDay
自JDK 1.1起已被弃用。 -
dep-ann
-
警告使用文档中带有
@deprecated
Javadoc注释但没有@Deprecated
注解的项目,例如:/** * @deprecated 自Java SE 7起,由{@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"); // 这里没有break语句。 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和macOS:
javac -Xlint:path -classpath /nonexistentpath Example.java
-
Windows:
javac -Xlint:path -classpath C:\nonexistentpath Example.java
-
-
processing
-
警告与注解处理相关的问题。当您有一个具有注解的类,并且使用无法处理该类型注解的注解处理器时,编译器会生成此警告。例如,以下是一个简单的注解处理器:
源文件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(); } }
源文件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
关键字。 -
this-escape
-
警告构造函数在子类初始化之前泄漏
this
。例如,这个类:public class MyClass { public MyClass() { System.out.println(this.hashCode()); } }
生成以下警告:
MyClass.java:3: warning: [this-escape] possible 'this' escape before subclass is fully initialized System.out.println(this.hashCode()); ^
当构造函数执行可能导致在构造函数返回之前调用子类方法时,会生成“this”逃逸警告。在这种情况下,子类方法将在未完全初始化的实例上操作。在上面的示例中,覆盖
hashCode()
以包含自己的字段的MyClass
的子类可能会在显示调用时产生不正确的结果。只有在可能存在超出当前模块(或包,如果没有模块)的子类时才会生成警告。因此,例如,最终和非公共类中的构造函数不会生成警告。
-
try
-
警告与
try
块的使用相关的问题,包括使用try-with-resources语句。例如,对以下语句生成警告,因为在try
块中声明的资源ac
未被使用:try ( AutoCloseable ac = getResource() ) { // do nothing}
-
unchecked
-
提供了Java语言规范要求的未经检查的转换警告的更多详细信息,例如:
List l = new ArrayList<Number>(); List<String> ls = l; // 未经检查的警告
在类型擦除期间,类型
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
的定义生成以下警告:警告: [varargs] 可能由参数化的可变参数类型 T 引起堆污染
当编译器遇到变长参数方法时,它将
varargs
形式参数转换为数组。然而,Java编程语言不允许创建参数化类型的数组。在方法ArrayBuilder.addToList
中,编译器将varargs
形式参数T...
转换为形式参数T[]
,即一个数组。然而,由于类型擦除,编译器将varargs
形式参数转换为Object[]
元素。因此,存在堆污染的可能性。