注解(一种元数据形式)提供有关不属于程序自己的程序的数据,注解对它们注解的代码的操做没有直接影响。html
注解有许多用途,其中包括:java
本课程介绍了可使用注解的位置,以及如何应用注解,Java平台标准版(Java SE API)中提供了哪些预约义注解类型,类型注解如何与可插拔类型系统结合使用来编写具备更强类型检查的代码,以及如何实现重复注解。程序员
在最简单的形式中,注解以下所示:正则表达式
@Entity
符号字符(@
)向编译器指示后面的内容是注解,在如下示例中,注解的名称为Override
:segmentfault
@Override void mySuperMethod() { ... }
注解能够包含元素,这些元素能够是命名的,也能够是未命名的,这些元素的值以下:api
@Author( name = "Benjamin Franklin", date = "3/27/2003" ) class MyClass() { ... }
或:数组
@SuppressWarnings(value = "unchecked") void myMethod() { ... }
若是只有一个名为value
的元素,则能够省略该名称,如:安全
@SuppressWarnings("unchecked") void myMethod() { ... }
若是注解没有元素,则能够省略括号,如前面的@Override
示例所示。oracle
也能够在同一声明上使用多个注解:框架
@Author(name = "Jane Doe") @EBook class MyClass { ... }
若是注解具备相同的类型,则称为重复注解:
@Author(name = "Jane Doe") @Author(name = "John Smith") class MyClass { ... }
从Java SE 8发行版开始,支持重复注解,有关更多信息,请参阅重复注解。
注解类型能够是Java SE API的java.lang
或java.lang.annotation
包中定义的类型之一,在前面的示例中,Override
和SuppressWarnings
是预约义的Java注解,也能够定义本身的注解类型,上一个示例中的Author
和Ebook
注解是自定义注解类型。
注解能够应用于声明:类、字段、方法和其余程序元素的声明,当在声明中使用时,按照惯例,每一个注解一般出如今它本身的行上。
从Java SE 8发行版开始,注解也能够应用于类型的使用,这里有些例子:
类实例建立表达式:
new @Interned MyObject();
输入:
myString = (@NonNull String) str;
implements
子句:
class UnmodifiableList<T> implements @Readonly List<@Readonly T> { ... }
抛出的异常声明:
void monitorTemperature() throws @Critical TemperatureException { ... }
这种形式的注解称为类型注解,有关更多信息,请参阅类型注解和可插拔类型系统。
许多注解替换代码中的注释。
假设一个软件组一般在每一个类的开头都带有注释,这些注释提供了重要的信息:
public class Generation3List extends Generation2List { // Author: John Doe // Date: 3/17/2002 // Current revision: 6 // Last modified: 4/12/2004 // By: Jane Doe // Reviewers: Alice, Bill, Cindy // class code goes here }
要使用注解添加相同的元数据,必须先定义注解类型,这样作的语法是:
@interface ClassPreamble { String author(); String date(); int currentRevision() default 1; String lastModified() default "N/A"; String lastModifiedBy() default "N/A"; // Note use of array String[] reviewers(); }
注解类型定义相似于接口定义,其中关键字interface
前面带有at符号(@
)(@ = AT
,如在注解类型中),注解类型是一种接口形式,将在后面的课程中介绍,目前,你不须要了解接口。
前一个注解定义的主体包含注解类型元素声明,它看起来很像方法,请注意,他们能够定义可选的默认值。
定义注解类型后,你可使用该类型的注解,并填入值,以下所示:
@ClassPreamble ( author = "John Doe", date = "3/17/2002", currentRevision = 6, lastModified = "4/12/2004", lastModifiedBy = "Jane Doe", // Note array notation reviewers = {"Alice", "Bob", "Cindy"} ) public class Generation3List extends Generation2List { // class code goes here }
注意:要使@ClassPreamble
中的信息出如今Javadoc
生成的文档中,必须使用@Documented
注解来注解@ClassPreamble
定义:
// import this to use @Documented import java.lang.annotation.*; @Documented @interface ClassPreamble { // Annotation element definitions }
Java SE API中预约义了一组注解类型,某些注解类型用于Java编译器使用,有些注解类型应用于其余注解。
java.lang
中定义的预约义注解类型是@Deprecated
、@Override
和@SuppressWarnings
。
@Deprecated:@Deprecated注解表示标记已弃用和不该该再使用的元素,只要程序使用带有@Deprecated
注解的方法、类或字段,编译器就会生成警告。当弃用元素时,也应使用Javadoc @deprecated
标记对其进行记录,如如下示例所示。在Javadoc注释和注解中使用at符号(@
)并不是巧合:它们在概念上是相关的,另请注意,Javadoc标记以小写d
开头,注解以大写D
开头。
// Javadoc comment follows /** * @deprecated * explanation of why it was deprecated */ @Deprecated static void deprecatedMethod() { }
@Override:@Override注解通知编译器该元素旨在覆盖超类中声明的元素,将在接口和继承中讨论重写方法。
// mark method as a superclass method // that has been overridden @Override int overriddenMethod() { }
虽然在重写方法时不须要使用此注解,但它有助于防止出错,若是使用@Override
标记的方法没法正确覆盖其某个超类中的方法,则编译器会生成错误。
@SuppressWarnings:@SuppressWarnings注解告诉编译器抑制它将生成的特定警告,在如下示例中,使用了弃用的方法,编译器一般会生成警告,可是,在这种状况下,注解会致使警告被抑制。
// use a deprecated method and tell // compiler not to generate a warning @SuppressWarnings("deprecation") void useDeprecatedMethod() { // deprecation warning // - suppressed objectOne.deprecatedMethod(); }
每一个编译器警告都属于一个类别,Java语言规范列出了两个类别:deprecation
和unchecked
,当在泛型出现以前编写的遗留代码接口时,可能会发生unchecked
警告,要禁止多种类别的警告,请使用如下语法:
@SuppressWarnings({"unchecked", "deprecation"})
@SafeVarargs:@SafeVarargs注解在应用于方法或构造函数时断言代码不对其varargs
参数执行可能不安全的操做,使用此注解类型时,与varargs
使用相关的未经检查的警告被抑制。
@FunctionalInterface:Java SE 8中引入的@FunctionalInterface注解,表示类型声明旨在成为Java语言规范定义的功能接口。
应用于其余注解的注解称为元注解,java.lang.annotation
中定义了几种元注解类型。
@Retention:@Retention注解指定标记的注解的存储方式:
RetentionPolicy.SOURCE
— 标记的注解仅保留在源级别中,并被编译器忽略。RetentionPolicy.CLASS
— 标记的注解在编译时由编译器保留,但Java虚拟机(JVM)会忽略。RetentionPolicy.RUNTIME
— 标记的注解由JVM保留,所以运行时环境可使用它。@Documented:@Documented注解代表,不管什么时候使用指定的注解,都应使用Javadoc工具记录这些元素(默认状况下,注解不包含在Javadoc中),有关更多信息,请参阅Javadoc工具页面。
@Target:@Target注解标记另外一个注解,以限制能够应用注解的Java元素类型,目标注解指定如下元素类型之一做为其值:
ElementType.ANNOTATION_TYPE
能够应用于注解类型。ElementType.CONSTRUCTOR
能够应用于构造函数。ElementType.FIELD
能够应用于字段或属性。ElementType.LOCAL_VARIABLE
能够应用于局部变量。ElementType.METHOD
能够应用于方法级注解。ElementType.PACKAGE
能够应用于包声明。ElementType.PARAMETER
能够应用于方法的参数。ElementType.TYPE
能够应用于类的任何元素。@Inherited:@Inherited注解代表注解类型能够从超类继承(默认状况下不是这样),当用户查询注解类型而且该类没有此类型的注解时,将查询类的超类以获取注解类型,此注解仅适用于类声明。
@Repeatable:Java SE 8中引入的@Repeatable注解代表标记的注解能够屡次应用于相同的声明或类型使用,有关更多信息,请参阅重复注解。
在Java SE 8发行版以前,注解只能应用于声明,从Java SE 8发行版开始,注解也能够应用于任何类型的使用,这意味着能够在任何使用类型的地方使用注解。使用类型的一些示例包括类实例建立表达式(new
)、类型转换、implements
子句和throws
子句,这种注解形式称为类型注解,注解基础知识中提供了几个示例。
建立类型注解是为了支持改进的Java程序分析,以确保更强的类型检查,Java SE 8版本不提供类型检查框架,但它容许你编写(或下载)类型检查框架,该框架实现为与Java编译器结合使用的一个或多个可插拔模块。
例如,你但愿确保程序中的特定变量永远不会分配给null
,你想避免触发NullPointerException
,你能够编写自定义插件来检查此问题,而后,你将修改代码以注解该特定变量,代表它永远不会被赋值为null
,变量声明可能以下所示:
@NonNull String str;
当你编译代码(包括命令行中的NonNull
模块)时,编译器会在检测到潜在问题时输出警告,容许你修改代码以免错误,在更正代码以移除全部警告后,程序运行时不会发生此特定错误。
你可使用多个类型检查模块,其中每一个模块检查不一样类型的错误,经过这种方式,你能够在Java类型系统的基础上构建,在你但愿的时间和位置添加特定的检查。
经过明智地使用类型注解和可插拔类型检查器,你能够编写更强大且更不容易出错的代码。
在许多状况下,你没必要编写本身的类型检查模块,有第三方为你完成了这项工做,例如,你可能但愿利用华盛顿大学建立的Checker Framework,该框架包括NonNull
模块、正则表达式模块和互斥锁模块,有关更多信息,请参阅Checker Framework。
在某些状况下,你但愿将相同的注解应用于声明或类型用途,从Java SE 8发行版开始,重复注解使你能够执行此操做。
例如,你正在编写代码以使用计时器服务,该服务使你可以在给定时间或某个计划上运行方法,相似于UNIX cron服务,如今你要设置一个计时器来运行一个方法doPeriodicCleanup
,在该月的最后一天和每一个星期五晚上11点运行,要设置要运行的计时器,请建立一个@Schedule
注解并将其应用于doPeriodicCleanup
方法两次,第一次使用指定月份的最后一天,第二次使用指定星期五晚上11点,以下面的代码示例所示:
@Schedule(dayOfMonth="last") @Schedule(dayOfWeek="Fri", hour="23") public void doPeriodicCleanup() { ... }
前面的示例将注解应用于方法,你能够在使用标准注解的任何位置重复注解,例如,你有一个用于处理未受权访问异常的类,你可使用一个@Alert
注解为管理者注解该类,为管理员注解另外一个注解:
@Alert(role="Manager") @Alert(role="Administrator") public class UnauthorizedAccessException extends SecurityException { ... }
出于兼容性缘由,重复注解存储在由Java编译器自动生成的容器注解中,为了使编译器执行此操做,代码中须要两个声明。
注解类型必须使用@Repeatable
元注解进行标记,如下示例定义自定义@Schedule
可重复注解类型:
@Repeatable(Schedules.class) public @interface Schedule { String dayOfMonth() default "first"; String dayOfWeek() default "Mon"; int hour() default 12; }
@Repeatable
元注解的值(在括号中)是Java编译器生成的用于存储重复注解的容器注解的类型,在此示例中,包含注解类型是Schedules
,所以重复@Schedule
注解存储在@Schedules
注解中。
将相同的注解应用于声明而不首先声明它是可重复的,这会致使编译时错误。
包含注解类型必须具备带数组类型的value
元素,数组类型的组件类型必须是可重复的注解类型,包含注解类型的Schedules
的声明以下:
public @interface Schedules { Schedule[] value(); }
Reflection API中有几种可用于检索注解的方法,返回单个注解的方法(例如AnnotatedElement.getAnnotation(Class<T>))的行为未更改,由于若是存在所请求类型的一个注解,它们仅返回单个注解,若是存在多个所请求类型的注解,则能够经过首先获取其容器注解来获取它们,经过这种方式,遗留代码继续工做。Java SE 8中引入了其余方法,它们扫描容器注解以一次返回多个注解,例如AnnotatedElement.getAnnotationsByType(Class<T>),有关全部可用方法的信息,请参阅AnnotatedElement类规范。
设计注解类型时,必须考虑该类型注解的基数,如今可使用注解零次、一次,或者,若是注解的类型标记为@Repeatable
,则不止一次,经过使用@Target
元注解,还能够限制注解类型的使用位置。例如,你能够建立只能在方法和字段上使用的可重复注解类型,仔细设计注解类型很是重要,以确保使用注解的程序员发现它尽量灵活和强大。