注解(Annotation)是元数据的一种形式,它提供描述程序但不属于程序本身的数据。注解没有直接作用于所标注的代码本身的功能。
注解是在Java SE 5时引入的一个新特性,很多时候它被称为元数据特性。注解有以下几种功能:
- 提供信息给编译器 —— 注解能够编译器用来检测错误或抑制警告
- 编译时和部署时处理 —— 注解处理工具可以处理注解信息以此生成代码、XML文件等等
- 运行时处理 —— 一些注解能够在运行时被检查运用到
使用注解的好处:
- 每当你创建描述符性质的类或接口并涉及于重复性的工作,那通常你就可以考虑使用注解来简化与自动化该过程。
- 注解是在实际的源码级别保存所有程序相关的信息,而不是某种注释性的文字(comment),这使得代码更整洁,且便于维护。
注解基础
了解注解
当我们看到
@
这个符号的时候,它表明紧跟它的就是一个注解。@Override
、@Entity
、@SuppressWarnings
等等,它们都是注解。注解可以包含元素,我们可以给它指明名字或者直接忽略其名,同时可赋值于元素。
@SuppressWarnings(value={"unchecked"})
、@SuppressWarnings("unchecked")
如果注解自身没有元素,那么连括号也可以忽略。
@Override
、@Test
可以给同一声明使用多个注解
1
2
3
"unchecked") (
void f1(int parameter)Java SE 8中支持重复注解(repeating annotation)特性,即在同一处声明使用多个同一个注解。
1
2
3"gtr") (name=
"jack") (name=
class Foo { }
在哪里可以使用注解
通常注解可用于所有声明处:各种类型(类、接口、枚举、注解)、域、构造器、方法、局部变量、方法参数。
在Java SE 8中更为注解加入了类型注解(type annotation)的特性,它指明了注解可以用在使用类型的任何地方。
1 | new MyObject(); |
我们可以看到:在创建实例、强制类型转换、泛型类型参数、抛异常的声明,在这些地方都可以用上注解。
自定义注解
定义注解类型
注解类型是一种特殊的接口类型(此处的接口类型并非Java中interface
这个关键字所定义的类型)。
定义注解类型的语法如下:
{InterfaceModifier} @ interface Identifier AnnotationTypeBody
或
modifiers @interface AnnotationName {
elementDeclaration1
elementDeclaration2
...
}
并且注解元素声明都具有以下这种形式:
type elementName();
或
type elementName() default value;
注解类型的小细节:
java.lang.annotation.Annotation
是所有注解类型的父类型。- 注解类型不能被声明为泛型,并且它不能有
extends
子句(即不能进行继承扩展)。 - 任何类或接口中都不能再定义一个与它们同名的内部注解。
- 注解类型中可以包含注解元素、常量、类、接口这4种成员。
特殊注解类型:
- 没有元素的注解类型被称为标记注解类型(marker annotation type)。
- 注解类型中只有一个元素且该元素名为
value
;注解类型有多个元素,其中一个名为value
,其余元素都有默认值。
以上两种情况的注解类型被称为单元素注解类型(single-element annotation type)。
注解元素中,方法的返回类型只能是以下这些类型:
- 内置基础类型(primitive type)
String
Class<T>
的原生类型或其参数化类型- 枚举类型
- 注解类型
- 以上这些类型作成分的数组类型
现在来自定义一个注解。1
2
3
4
5
6
7
8
9public ClassPreamble {
String author();
String date();
int currentReversion() default 1;
String lastModified() default "N/A";
String lastModifiedBy() default "N/A";
// note use of array
String[] reviewers();
}
注解的定义看起来就像是接口的定义,它们都使用关键字interface
进行定义,只不过定义注解时必须要在这个关键字前多加一个符号@
。这个注解有6个元素,并且其中一些元素被指定了默认值。
使用
定义完了注解类型,那么我们就可以开始使用它们了。
使用注解类型都采用以下这种语法:
@AnnotaionName(elementName1=value1, elementName2=value2, ...)
注解是一个将信息与程序架构关联在一起的标识,但它在运行时没有其它副作用的。
注解分为3种类型:
- 普通注解(normal annotation):
@TypeName([ElementValuePairList])
。 - 标记注解(marker annotation):
@TypeName
。 - 单元素注解(single element annotation):
@TypeName(value = ElementValue)
在这3种注解,其实可以把标记注解和单元素注解看作普通注解,平时可以当这2种是普通注解的简化形式。
关于注解元素这块的小细节:
- 使用注解并指明元素值时,元素的顺序是无关紧要的,就是说元素的定义顺序与使用时的顺序可以不一样。
- 如果在使用注解时未指明某个元素的值且这个元素有默认值,那么就使用定义注解时的默认元素值。
- 当所定义的注解只有1个元素并且其元素名为
value
时,使用该注解时可以直接不写元素名而指定元素值。
现在就来使用之前定义的注解ClassPreamble
。
1 | ( |
注解里的信息看上去就像是我们平时查API文档时所看到的信息。那么我们怎么才能让注解中的信息输出在类Demo1
的API文档上?
解决办法肯定有,就是用@Documented
这个注解标注在我们的自定义注解@ClassPreamble
上。
1 | import java.lang.annotation.Documented; |
预定义注解类型
其实在Java SE API中就已经预定义了很多注解类型,这些注解类型包含于java.lang
或java.lang.annotation
这两个包中,一般我们都称它们为预定义注解类型(predefined annotation type)或标准注解类型(standard annotation type)。其中一些是提供给编译器使用的注解,而另一些是专职于标注其他注解的被称为元注解(meta annotaion)。
用于编译的注解
@Deprecated
凡是带
@Deprecated
注解的程序元素,它们都不再被开发者推荐使用的,即该注解表示此代码已经过时或者有危险性,而开发者已经提供了更好的解决方案给你使用。当你是开发者而又认为某些代码不应该再提供给接口用户使用,你就能用这个注解。
当你使用被该注解标注的代码,编译器会发出警告。
@SuppressWarnings
该注解可以禁用编译器所发出的某些警告。
当你在开发的时候清楚某些编译器警告是多余,你就可以用
@SuppressWarnings
标注有警告的地方,这样注解就会关闭这个警告。
@Override
- 该注解只应用在方法上,它的责任就是在编译时检测被标注的方法是否正确地被重载。