注解简介
Annotation(注解)就是Java提供了一种元程序中的元素关联任何信息或者任何元数据(metadata)的途径和方法。Annotion(注解)是一个接口,程序可以通过反射来获取指定程序元素的Annotion对象,然后通过Annotion对象来获取注解里面的元数据。
注解Annotation是Java5引入的新特性。它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。更通俗的意思是为程序的元素(类、方法、成员变量)加上更直观更明了的说明,这些说明信息是与程序的业务逻辑无关,并且是供指定的工具或框架使用的。
Annontation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中,它被置于注解项之前,中间没有分号(修饰符就是诸如public或者static之类的关键字)。每一个注解前面都会加上一个@符号。
注解原理
Annotation其实是一种接口。通过Java的反射机制相关的API来访问Annotation信息。相关类(框架或工具中的类)根据这些信息来决定如何使用该程序元素或改变它们的行为。
Annotation是不会影响程序代码的执行,无论Annotation怎么变化,代码都始终如一地执行。
Java编译器在工作时会忽略这些Annotation,因此在JVM 中这些Annotation是“不起作用”的,Java编译器对于包含注解和不包含注解的代码会生成相同的虚拟器指令。因此如果想在实际开发中使用注解,必须得有对Annotation进行访问和处理的工具才行,这也是使用注解的核心所在。
注解语法
注解由注解接口定义的:
<textarea wrap="soft" class="crayon-plain print-no eye-protector-processed" data-settings="dblclick" readonly="readonly" style="padding-top: 0px; padding-right: 5px; padding-left: 5px; border: 0px; color: rgb(0, 0, 0); max-width: 100%; width: 910.344px; overflow: hidden; margin: 0px; height: 110px; position: absolute; opacity: 0; border-radius: 0px; box-shadow: none; white-space: pre; word-wrap: normal; resize: none; tab-size: 4; z-index: 0; transition: background 0.3s ease; font-size: 12px !important; font-family: Monaco, MonacoRegular, "Courier New", monospace !important; line-height: 22px !important; background: rgb(193, 230, 198);"></textarea>
|
modifiers
@
interface
AnnotaionName
{
elementDeclaration01
;
elementDeclaration02
;
.
.
.
}
|
每个元素elementDeclaration的声明都具有下面这种形式:
<textarea wrap="soft" class="crayon-plain print-no eye-protector-processed" data-settings="dblclick" readonly="readonly" style="padding-top: 0px; padding-right: 5px; padding-left: 5px; border: 0px; color: rgb(0, 0, 0); max-width: 100%; width: 910.344px; overflow: hidden; margin: 0px; height: 66px; position: absolute; opacity: 0; border-radius: 0px; box-shadow: none; white-space: pre; word-wrap: normal; resize: none; tab-size: 4; z-index: 0; transition: background 0.3s ease; font-size: 12px !important; font-family: Monaco, MonacoRegular, "Courier New", monospace !important; line-height: 22px !important; background: rgb(193, 230, 198);"></textarea>
|
type
elementName
(
)
;
或
type
elementName
(
)
default
value
;
|
举个示例说明一下注解:
<textarea wrap="soft" class="crayon-plain print-no eye-protector-processed" data-settings="dblclick" readonly="readonly" style="padding-top: 0px; padding-right: 5px; padding-left: 5px; border: 0px; color: rgb(0, 0, 0); max-width: 100%; width: 910.344px; overflow: hidden; margin: 0px; height: 88px; position: absolute; opacity: 0; border-radius: 0px; box-shadow: none; white-space: pre; word-wrap: normal; resize: none; tab-size: 4; z-index: 0; transition: background 0.3s ease; font-size: 12px !important; font-family: Monaco, MonacoRegular, "Courier New", monospace !important; line-height: 22px !important; background: rgb(193, 230, 198);"></textarea>
|
public
@interface
Model
{
String
name
(
)
default
"_name"
;
int
age
(
)
default
0
;
}
|
元素的顺序无关紧要,下面两个注解是一样效果的
@Model(name=”admin”,age=20)或
@Model(age=20,name=”admin”)
如果某个元素的值未指定,那么就使用声明的默认值。
@Model(age=20)
元素name的默认值就是”_name”。
如果没有指定元素,要么注解中没有任何元素,要么所有元素都使用默认值,这时候可以不用指定括号了。
@Model与@Model(name=”_name”,age=0)是一样的。
如果一个注解只有一个元素,并且元素的名称为value,这时候可以忽略这个元素及等号,该种方式的注解一般称为单值注解。
示例如下:
<textarea wrap="soft" class="crayon-plain print-no eye-protector-processed" data-settings="dblclick" readonly="readonly" style="padding-top: 0px; padding-right: 5px; padding-left: 5px; border: 0px; color: rgb(0, 0, 0); max-width: 100%; width: 910.344px; overflow: hidden; margin: 0px; height: 66px; position: absolute; opacity: 0; border-radius: 0px; box-shadow: none; white-space: pre; word-wrap: normal; resize: none; tab-size: 4; z-index: 0; transition: background 0.3s ease; font-size: 12px !important; font-family: Monaco, MonacoRegular, "Courier New", monospace !important; line-height: 22px !important; background: rgb(193, 230, 198);"></textarea>
|
public
@interface
OnClick
{
int
value
(
)
default
0
;
}
|
@OnClick(value=R.id.btn)与@OnClick(R.id.btn)一样。
所有的注解都隐式地扩展自java.lang.annotation.Annotation
接口,该接口是一个常规接口,并不是一个注解接口,开发者不可以扩展注解接口。
注解接口中的元素声明实际上是方法声明。一个注解接口的方法不能有任何的参数和任何throws语句,并且也不能是泛型。
一个注解元素永远不能为null,默认值也不允许为null。可以使用其它默认值,如””或者Void.class。
如果元素值是一个数组,它的值需要用括号括起来,如果具有单值则可以忽略大括号,如下:
@Model(…,hobby={“reading”,”writting”})
数组单值如下:
@Model(…,hobby=”reading”)
一个注解可以引用另一个注解,例如:
@Model(ref=@Bean(name=”admin”),…)
一个项可以有多个注解,但是这些注解必须是属于不同类型
<textarea wrap="soft" class="crayon-plain print-no eye-protector-processed" data-settings="dblclick" readonly="readonly" style="padding-top: 0px; padding-right: 5px; padding-left: 5px; border: 0px; color: rgb(0, 0, 0); max-width: 100%; width: 910.344px; overflow: hidden; margin: 0px; height: 110px; position: absolute; opacity: 0; border-radius: 0px; box-shadow: none; white-space: pre; word-wrap: normal; resize: none; tab-size: 4; z-index: 0; transition: background 0.3s ease; font-size: 12px !important; font-family: Monaco, MonacoRegular, "Courier New", monospace !important; line-height: 22px !important; background: rgb(193, 230, 198);"></textarea>
|
@Target
(
ElementType
.
METHOD
)
@Retention
(
RetentionPolicy
.
RUNTIME
)
public
@interface
OnClick
{
int
value
(
)
default
0
;
}
|
注解可以声明的类型如下:
- 所有基本数据类型(int,float,boolean,byte,double,char,long,short)
- String类型
- Class类型
- enum类型
- Annotation类型
- 以上所有类型的数组(有数组组成的数组除外)
下面是一个合法声明的示例:
<textarea wrap="soft" class="crayon-plain print-no eye-protector-processed" data-settings="dblclick" readonly="readonly" style="padding-top: 0px; padding-right: 5px; padding-left: 5px; border: 0px; color: rgb(0, 0, 0); max-width: 100%; width: 910.344px; overflow: hidden; margin: 0px; height: 220px; position: absolute; opacity: 0; border-radius: 0px; box-shadow: none; white-space: pre; word-wrap: normal; resize: none; tab-size: 4; z-index: 0; transition: background 0.3s ease; font-size: 12px !important; font-family: Monaco, MonacoRegular, "Courier New", monospace !important; line-height: 22px !important; background: rgb(193, 230, 198);"></textarea>
|
public
@interface
BugReport
{
enum
Status
{
SUCCESS
,
ERROR
,
LOADING
}
;
boolean
flag
(
)
default
false
;
int
value
(
)
default
0
;
String
name
(
)
default
""
;
Class
<
?
>
clazz
(
)
default
Void
.
class
;
Status
status
(
)
default
Status
.
LOADING
;
String
[
]
array
(
)
;
Reference
ref
(
)
default
@Reference
(
)
;
//注解类型
}
|
可以向注解中添加如下的项:
- 包
- 类(包括enum)
- 接口(包括注解接口)
- 方法
- 构造器
- 实力域
- 局部变量
- 参数变量
注解类型
JavaSE在java.lang、java.lang.annotation和javax.annotation包中定义了大量注解接口。其中四个是元注解,用于描述注解接口的行为属性。
注解接口 |
应用场合 |
目的 |
Deprecated |
全部 |
将项标记为过时的 |
SuppressWarnings |
除了包和注解之外的所有情况 |
组织某个给定类型的警告信息 |
Override |
方法 |
检测该方法是否覆盖了某一个超类的方法 |
PostConstruct PreDestroy |
被标记的方法应该在构造之后或者移除之前立即被调用 |
XXXXX |
Resource |
类、接口、方法、域 |
在类或接口上:标记为在其他地方要用到的资源。在方法或域上:为“注入”而标记 |
Resources |
类、接口 |
一个资源数组 |
Generated |
全部 |
提供代码生成工具使用 |
Target |
注解 |
指明可以应用这个注解的哪些项 |
Retention |
注解 |
指明这个注解可以保留多久 |
Documented |
注解 |
指明这个注解应该包含在注解项的文档中 |
Inherited |
注解 |
指明当这个注解应用于一个类的时候,能够自动被它的子类继承 |
@Deprecated注解表示该项是不被建议使用的。所以当我们使用一个已过时的项时,编译器将会发出警告。
@SuppressWarnings注解会告知编译器阻止特殊类型的警告信息。
@SuppressWarnings(“unchecked”)
@Override注解只能用在方法上。编译器会检查一个方法是否正在覆盖了父类的方法。
其它的几个注解项这里就不做解释了。
元注解
@Target元注解可以应用于一个注解,以限制该注解可以应用到哪些项上面。例如:
<textarea wrap="soft" class="crayon-plain print-no eye-protector-processed" data-settings="dblclick" readonly="readonly" style="padding-top: 0px; padding-right: 5px; padding-left: 5px; border: 0px; color: rgb(0, 0, 0); max-width: 100%; width: 910.344px; overflow: hidden; margin: 0px; height: 44px; position: absolute; opacity: 0; border-radius: 0px; box-shadow: none; white-space: pre; word-wrap: normal; resize: none; tab-size: 4; z-index: 0; transition: background 0.3s ease; font-size: 12px !important; font-family: Monaco, MonacoRegular, "Courier New", monospace !important; line-height: 22px !important; background: rgb(193, 230, 198);"></textarea>
|
@Target
(
{
ElementType
.
TYPE
,
ElementType
.
METHOD
}
)
public
@interface
Model
|
@Target注解类型如下表:
元素类型 |
注解使用场合 |
ANNOTATION_TYPE |
注解类型声明 |
PACKAGE |
包 |
TYPE |
类(包括enum)及接口(包括注解类型) |
METHOD |
方法 |
CONSTRUCTOR |
构造器 |
FIELD |
成员域(包括enum常量) |
PARAMETER |
方法或构造器参数 |
LOCAL_VARIABLE |
局部变量 |
一条没有@Target限制的注解可以应用于任何项上面。编译器会检查是否将一个注解只应用到了某个允许的项上面。
@Retention用于指定一个注解可以停留多长时间。其的默认值是RetentionPolicy.CLASS。
保留规则 |
描述 |
SOURCE |
注解会被编译器忽略,并只会保留在源代码中 |
CLASS |
注解会通过编译驻留在CLASS文件,但会被JVM在运行时忽略,正因为如此,其在运行时不可见 |
RUNTIME |
注解会被JVM获取,并在运行时通过反射获取 |
@Inherited元注解只能应用于对类的注解,它所有的子类都会自动具有同样的注解。这并不代表接口就不可以使用,在接口上使用该注解,只是说该注解不会起任何作用,其实现类不会继承接口中的任何注解。
@Documented 将此注解包含在 javadoc 中。
自定义注解
很多第三方框架都是支持注解操作的,如Android xUtils以及Retrofit、Hibernate等。在Android开发中有许多针对数据库操作的ORM框架,如xUtils和GreenDAO。下面通过一个非常简单的示例查看一下如何通过注解来实现ORM机制。
先自定义一个注解@Table,通过注解解析器我们可以构建一个数据库表格。
<textarea wrap="soft" class="crayon-plain print-no eye-protector-processed" data-settings="dblclick" readonly="readonly" style="padding-top: 0px; padding-right: 5px; padding-left: 5px; border: 0px; color: rgb(0, 0, 0); max-width: 100%; width: 910.344px; overflow: hidden; margin: 0px; height: 110px; position: absolute; opacity: 0; border-radius: 0px; box-shadow: none; white-space: pre; word-wrap: normal; resize: none; tab-size: 4; z-index: 0; transition: background 0.3s ease; font-size: 12px !important; font-family: Monaco, MonacoRegular, "Courier New", monospace !important; line-height: 22px !important; background: rgb(193, 230, 198);"></textarea>
|
@Target
(
value
=
{
ElementType
.
METHOD
,
ElementType
.
TYPE
}
)
@Retention
(
RetentionPolicy
.
RUNTIME
)
public
@interface
Table
{
String
name
(
)
;
}
|
下面是表格列的注解@Column,通过该注解我们可以解析出属性通表格列的对应关系。
<textarea wrap="soft" class="crayon-plain print-no eye-protector-processed" data-settings="dblclick" readonly="readonly" style="padding-top: 0px; padding-right: 5px; padding-left: 5px; border: 0px; color: rgb(0, 0, 0); max-width: 100%; width: 910.344px; overflow: hidden; margin: 0px; height: 154px; position: absolute; opacity: 0; border-radius: 0px; box-shadow: none; white-space: pre; word-wrap: normal; resize: none; tab-size: 4; z-index: 0; transition: background 0.3s ease; font-size: 12px !important; font-family: Monaco, MonacoRegular, "Courier New", monospace !important; line-height: 22px !important; background: rgb(193, 230, 198);"></textarea>
|
@Target
(
ElementType
.
FIELD
)
@Retention
(
RetentionPolicy
.
RUNTIME
)
public
@interface
Column
{
String
column
(
)
;
String
type
(
)
;
int
length
(
)
;
}
|
两个自定义注解已经定义好了,通过文章开始部分分析我们知道,如果没有注解解析器,注解也就没有任何实际意义了。
Java反射API包含了许多方法来在运行时从类,方法或者其它元素获取注解。接口AnnotatedElement包含了大部分重要的方法,如下:
- boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)如果该项具有给定类型的注解,则返回true。
- <T extends Annotation> T getAnnotation(Class<T> annotationClass)获得给定类型的注解,如果该项不具有该类型的注解,则返回null。
- Annotation[] getAnnotations()获得作用于该项的所有注解,包括继承而来的注解。如果没有任何注解,将返回一个长度为0的数组。
- Annotation[] getDeclaredAnnotations()获得为该项声明的所有注解,不包括继承而来的注解。
通过上面介绍给的几个获取注解的方法,通过反射我们就很容易实现一个注解解析器。
<textarea wrap="soft" class="crayon-plain print-no eye-protector-processed" data-settings="dblclick" readonly="readonly" style="padding-top: 0px; padding-right: 5px; padding-left: 5px; border: 0px; color: rgb(0, 0, 0); max-width: 100%; width: 910.344px; overflow: hidden; margin: 0px; height: 396px; position: absolute; opacity: 0; border-radius: 0px; box-shadow: none; white-space: pre; word-wrap: normal; resize: none; tab-size: 4; z-index: 0; transition: background 0.3s ease; font-size: 12px !important; font-family: Monaco, MonacoRegular, "Courier New", monospace !important; line-height: 22px !important; background: rgb(193, 230, 198);"></textarea>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
ublic
class
TableUtils
{
public
static
void
parse
(
Class
<
?
>
clazz
)
{
//table
Table
table
=
clazz
.
getAnnotation
(
Table
.
class
)
;
System
.
out
.
println
(
"表格名称:"
+
table
.
name
(
)
)
;
//column
Field
fields
[
]
=
clazz
.
getDeclaredFields
(
)
;
for
(
Field
field
:
fields
)
{
Column
column
=
field
.
getAnnotation
(
Column
.
class
)
;
System
.
out
.
println
(
"列名:"
+
column
.
column
(
)
+
" 类型:"
+
column
.
type
(
)
+
" 长度:"
+
column
.
length
(
)
)
;
}
}
}
//表格名称:t_student
//列名:_id 类型:int 长度:10
//列名:_name 类型:varchar 长度:20
//列名:_age 类型:int 长度:3
|
注解导图
图片来自网络

小结
在学习Android的过程中发现很多第三方或者官方的库都在使用注解,这次抽些时间系统的整理了一下相关知识,还算比较全面。在示例源代码中简单自定义了一个按钮事件的注解,可以直接在处理函数上面注解上相应的按钮即可。
示例源代码下载
本文地址:www.sunnyang.com/413.html
参考资料
深入理解Java:注解(Annotation)自定义注解入门
Java 注解指导手册 – 终极向导
Java注解教程及自定义注解
Java注解(一)
理解Java基础之注解Annotation
********************************************************************
其他文章:
怎样优雅地使用java注解?