摘要
Java Annotation是JDK5.0引入的一种正文机制。
网上很多对于Java Annotation的文章,看得人目迷五色。Java Annotation原本很简略的,后果说的人没说分明;弄的看的人更加迷糊。
我依照本人的思路,对Annotation进行了整顿。了解 Annotation 的要害,是了解Annotation的语法和用法,对这些内容,我都进行了具体阐明;了解Annotation的语法和用法之后,再看Annotation的框架图,可能有更粗浅领会。废话就说这么多,上面开始对Annotation进行阐明。若您发现文章中存在谬误或有余的中央,心愿您能指出!
第1局部 Annotation架构
先看看Annotation的架构图:
从中,咱们能够看出:
- 1个Annotation 和 1个RetentionPolicy关联。
能够了解为:每1个Annotation对象,都会有惟一的RetentionPolicy属性。 - 1个Annotation 和 1~n个ElementType关联。
能够了解为:对于每1个Annotation对象,能够有若干个ElementType属性。 - Annotation 有许多实现类,包含:Deprecated, Documented, Inherited, Override等等。
Annotation 的每一个实现类,都“和1个RetentionPolicy关联”并且“和1~n个ElementType关联”。
上面,我先介绍框架图的左半边(如下图),即Annotation, RetentionPolicy, ElementType;而后在就Annotation的实现类进行举例说明。
第2局部 Annotation组成部分
1 annotation组成成分
java annotation 的组成中,有3个十分重要的骨干类。它们别离是:
(01) Annotation.java
package java.lang.annotation;
public interface Annotation {
boolean equals(Object obj);
int hashCode();
String toString();
Class<? extends Annotation> annotationType();
}
(02) ElementType.java
package java.lang.annotation;
public enum ElementType {
TYPE, /* 类、接口(包含正文类型)或枚举申明 */
FIELD, /* 字段申明(包含枚举常量) */
METHOD, /* 办法申明 */
PARAMETER, /* 参数申明 */
CONSTRUCTOR, /* 构造方法申明 */
LOCAL_VARIABLE, /* 局部变量申明 */
ANNOTATION_TYPE, /* 正文类型申明 */
PACKAGE /* 包申明 */
}
(03) RetentionPolicy.java
package java.lang.annotation;
public enum RetentionPolicy {
SOURCE, /* Annotation信息仅存在于编译器解决期间,编译器解决完之后就没有该Annotation信息了 */
CLASS, /* 编译器将Annotation存储于类对应的.class文件中。默认行为 */
RUNTIME /* 编译器将Annotation存储于class文件中,并且可由JVM读入 */
}
阐明:
(01) Annotation 就是个接口。
“每1个Annotation” 都与 “1个RetentionPolicy”关联,并且与 “1~n个ElementType”关联。能够艰深的了解为:每1个Annotation对象,都会有惟一的RetentionPolicy属性;至于ElementType属性,则有1~n个。
(02) ElementType 是Enum枚举类型,它用来指定Annotation的类型。
“每1个Annotation” 都与 “1~n个ElementType”关联。当Annotation与某个ElementType关联时,就意味着:Annotation有了某种用处。
例如,若一个Annotation对象是METHOD类型,则该Annotation只能用来润饰办法。
(03) RetentionPolicy 是Enum枚举类型,它用来指定Annotation的策略。艰深点说,就是不同RetentionPolicy类型的Annotation的作用域不同。
“每1个Annotation” 都与 “1个RetentionPolicy”关联。
a) 若Annotation的类型为 SOURCE,则意味着:Annotation仅存在于编译器解决期间,编译器解决完之后,该Annotation就没用了。
例如,“ @Override ”标记就是一个Annotation。当它润饰一个办法的时候,就意味着该办法笼罩父类的办法;并且在编译期间会进行语法查看!编译器解决完后,“@Override”就没有任何作用了。
b) 若Annotation的类型为 CLASS,则意味着:编译器将Annotation存储于类对应的.class文件中,它是Annotation的默认行为。
c) 若Annotation的类型为 RUNTIME,则意味着:编译器将Annotation存储于class文件中,并且可由JVM读入。
这时,只须要记住“每1个Annotation” 都与 “1个RetentionPolicy”关联,并且与 “1~n个ElementType”关联。学完前面的内容之后,再回头看这些内容,会更容易了解。
第3局部 java自带的Annotation
了解了下面的3个类的作用之后,咱们接下来能够解说Annotation实现类的语法定义了。
1 Annotation通用定义
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation1 {
}
阐明:
下面的作用是定义一个Annotation,它的名字是MyAnnotation1。定义了MyAnnotation1之后,咱们能够在代码中通过“@MyAnnotation1”来应用它。
其它的,@Documented, @Target, @Retention, @interface都是来润饰MyAnnotation1的。上面别离说说它们的含意:
(01) @interface
应用@interface定义注解时,意味着它实现了java.lang.annotation.Annotation接口,即该注解就是一个Annotation。
定义Annotation时,@interface是必须的。
留神:它和咱们通常的implemented实现接口的办法不同。Annotation接口的实现细节都由编译器实现。通过@interface定义注解后,该注解不能继承其余的注解或接口。
(02) @Documented
类和办法的Annotation在缺省状况下是不呈现在javadoc中的。如果应用@Documented润饰该Annotation,则示意它能够呈现在javadoc中。
定义Annotation时,@Documented可有可无;若没有定义,则Annotation不会呈现在javadoc中。
(03) @Target(ElementType.TYPE)
后面咱们说过,ElementType 是Annotation的类型属性。而@Target的作用,就是来指定Annotation的类型属性。
@Target(ElementType.TYPE) 的意思就是指定该Annotation的类型是ElementType.TYPE。这就意味着,MyAnnotation1是来润饰“类、接口(包含正文类型)或枚举申明”的注解。
定义Annotation时,@Target可有可无。若有@Target,则该Annotation只能用于它所指定的中央;若没有@Target,则该Annotation能够用于任何中央。
(04) @Retention(RetentionPolicy.RUNTIME)
后面咱们说过,RetentionPolicy 是Annotation的策略属性,而@Retention的作用,就是指定Annotation的策略属性。
@Retention(RetentionPolicy.RUNTIME) 的意思就是指定该Annotation的策略是RetentionPolicy.RUNTIME。这就意味着,编译器会将该Annotation信息保留在.class文件中,并且能被虚拟机读取。
定义Annotation时,@Retention可有可无。若没有@Retention,则默认是RetentionPolicy.CLASS。
2 java自带的Annotation
通过下面的示例,咱们能了解:@interface用来申明Annotation,@Documented用来示意该Annotation是否会呈现在javadoc中, @Target用来指定Annotation的类型,@Retention用来指定Annotation的策略。
了解这一点之后,咱们就很容易了解java中自带的Annotation的实现类,即Annotation架构图的右半边。如下图:
java 罕用的Annotation:
@Deprecated -- @Deprecated 所标注内容,不再被倡议应用。
@Override -- @Override 只能标注办法,示意该办法笼罩父类中的办法。
@Documented -- @Documented 所标注内容,能够呈现在javadoc中。
@Inherited -- @Inherited只能被用来标注“Annotation类型”,它所标注的Annotation具备继承性。
@Retention -- @Retention只能被用来标注“Annotation类型”,而且它被用来指定Annotation的RetentionPolicy属性。
@Target -- @Target只能被用来标注“Annotation类型”,而且它被用来指定Annotation的ElementType属性。
@SuppressWarnings -- @SuppressWarnings 所标注内容产生的正告,编译器会对这些正告放弃静默。
因为“@Deprecated和@Override”相似,“@Documented, @Inherited, @Retention, @Target”相似;上面,咱们只对@Deprecated, @Inherited, @SuppressWarnings 这3个Annotation进行阐明。
2.1 @Deprecated
@Deprecated 的定义如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Deprecated {
}
阐明:
(01) @interface — 它的用来润饰Deprecated,意味着Deprecated实现了java.lang.annotation.Annotation接口;即Deprecated就是一个注解。
(02) @Documented — 它的作用是阐明该注解能呈现在javadoc中。
(03) @Retention(RetentionPolicy.RUNTIME) — 它的作用是指定Deprecated的策略是RetentionPolicy.RUNTIME。这就意味着,编译器会将Deprecated的信息保留在.class文件中,并且能被虚拟机读取。
(04) @Deprecated 所标注内容,不再被倡议应用。
例如,若某个办法被 @Deprecated 标注,则该办法不再被倡议应用。如果有开发人员试图应用或重写被@Deprecated标示的办法,编译器会给相应的提示信息。示例如下:
源码如下(DeprecatedTest.java):
View Code
阐明:
下面是eclipse中的截图,比拟类中 “getString1() 和 getString2()” 以及 “testDate() 和 testCalendar()” 。
(01) getString1() 被@Deprecated标注,意味着倡议不再应用getString1();所以getString1()的定义和调用时,都会一横线。这一横线是eclipse()对@Deprecated办法的解决。
getString2() 没有被@Deprecated标注,它的显示失常。
(02) testDate() 调用了Date的相干办法,而java曾经倡议不再应用Date操作日期/工夫。因而,在调用Date的API时,会产生正告信息,途中的warnings。
testCalendar() 调用了Calendar的API来操作日期/工夫,java倡议用Calendar取代Date。因而,操作Calendar不回产生warning。
2.2 @Inherited
@Inherited 的定义如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
阐明:
(01) @interface — 它的用来润饰Inherited,意味着Inherited实现了java.lang.annotation.Annotation接口;即Inherited就是一个注解。
(02) @Documented — 它的作用是阐明该注解能呈现在javadoc中。
(03) @Retention(RetentionPolicy.RUNTIME) — 它的作用是指定Inherited的策略是RetentionPolicy.RUNTIME。这就意味着,编译器会将Inherited的信息保留在.class文件中,并且能被虚拟机读取。
(04) @Target(ElementType.ANNOTATION_TYPE) — 它的作用是指定Inherited的类型是ANNOTATION_TYPE。这就意味着,@Inherited只能被用来标注“Annotation类型”。
(05) @Inherited 的含意是,它所标注的Annotation将具备继承性。
假如,咱们定义了某个Annotaion,它的名称是MyAnnotation,并且MyAnnotation被标注为@Inherited。当初,某个类Base应用了MyAnnotation,则Base具备了“具备了注解MyAnnotation”;当初,Sub继承了Base,因为MyAnnotation是@Inherited的(具备继承性),所以,Sub也“具备了注解MyAnnotation”。
@Inherited的应用示例
源码如下(InheritableSon.java):
1 /**
2 * @Inherited 演示示例
3 *
4 * @author skywang
5 * @email kuiwu-wang@163.com
6 */
7 package com.skywang.annotation;
8
9 import java.lang.annotation.Target;
10 import java.lang.annotation.ElementType;
11 import java.lang.annotation.Retention;
12 import java.lang.annotation.RetentionPolicy;
13 import java.lang.annotation.Inherited;
14
15 /**
16 * 自定义的Annotation。
17 */
18 @Target(ElementType.TYPE)
19 @Retention(RetentionPolicy.RUNTIME)
20 @Inherited
21 @interface Inheritable
22 {
23 }
24
25 @Inheritable
26 class InheritableFather
27 {
28 public InheritableFather() {
29 // InheritableBase是否具备 Inheritable Annotation
30 System.out.println("InheritableFather:"+InheritableFather.class.isAnnotationPresent(Inheritable.class));
31 }
32 }
33
34 /**
35 * InheritableSon 类只是继承于 InheritableFather,
36 */
37 public class InheritableSon extends InheritableFather
38 {
39 public InheritableSon() {
40 super(); // 调用父类的构造函数
41 // InheritableSon类是否具备 Inheritable Annotation
42 System.out.println("InheritableSon:"+InheritableSon.class.isAnnotationPresent(Inheritable.class));
43 }
44
45 public static void main(String[] args)
46 {
47 InheritableSon is = new InheritableSon();
48 }
49 }
运行后果:
InheritableFather:true
InheritableSon:true
当初,咱们对InheritableSon.java进行批改:正文掉“Inheritable的@Inherited注解”。
源码如下(InheritableSon.java):
View Code
运行后果:
InheritableFather:true
InheritableSon:false
比照下面的两个后果,咱们发现:当注解Inheritable被@Inherited标注时,它具备继承性。否则,没有继承性。
2.3 @SuppressWarnings
@SuppressWarnings 的定义如下:
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
阐明:
(01) @interface — 它的用来润饰SuppressWarnings,意味着SuppressWarnings实现了java.lang.annotation.Annotation接口;即SuppressWarnings就是一个注解。
(02) @Retention(RetentionPolicy.SOURCE) — 它的作用是指定SuppressWarnings的策略是RetentionPolicy.SOURCE。这就意味着,SuppressWarnings信息仅存在于编译器解决期间,编译器解决完之后SuppressWarnings就没有作用了。
(03) @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) — 它的作用是指定SuppressWarnings的类型同时包含TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE。
TYPE意味着,它能标注“类、接口(包含正文类型)或枚举申明”。
FIELD意味着,它能标注“字段申明”。
METHOD意味着,它能标注“办法”。
PARAMETER意味着,它能标注“参数”。
CONSTRUCTOR意味着,它能标注“构造方法”。
LOCAL_VARIABLE意味着,它能标注“局部变量”。
(04) String[] value(); 意味着,SuppressWarnings能指定参数
(05) SuppressWarnings 的作用是,让编译器对“它所标注的内容”的某些正告放弃静默。例如,”@SuppressWarnings(value={“deprecation”, “unchecked”})” 示意对“它所标注的内容”中的 “SuppressWarnings不再倡议应用正告”和“未查看的转换时的正告”保持沉默。示例如下:
源码如下(SuppressWarningTest.java):
View Code
阐明:
(01) 右边的图中,没有应用 @SuppressWarnings(value={“deprecation”}) , 而Date属于java不再倡议应用的类。因而,调用Date的API时,会产生正告。
而左边的途中,应用了 @SuppressWarnings(value={“deprecation”})。因而,编译器对“调用Date的API产生的正告”保持沉默。
补充:SuppressWarnings 罕用的关键字的表格
deprecation -- 应用了不赞成应用的类或办法时的正告
unchecked -- 执行了未查看的转换时的正告,例如当应用汇合时没有用泛型 (Generics) 来指定汇合保留的类型。
fallthrough -- 当 Switch 程序块间接通往下一种状况而没有 Break 时的正告。
path -- 在类门路、源文件门路等中有不存在的门路时的正告。
serial -- 当在可序列化的类上短少 serialVersionUID 定义时的正告。
finally -- 任何 finally 子句不能失常实现时的正告。
all -- 对于以上所有状况的正告。
第4局部 Annotation 的作用
Annotation 是一个辅助类,它在Junit、Struts、Spring等工具框架中被宽泛应用。
咱们在编程中常常会应用到的Annotation作用有:
1 编译查看
Annotation具备“让编译器进行编译查看的作用”。
例如,@SuppressWarnings, @Deprecated和@Override都具备编译查看作用。
(01) 对于@SuppressWarnings和@Deprecated,曾经在“第3局部”中具体介绍过了。这里就不再举例说明了。
(02) 若某个办法被 @Override的 标注,则意味着该办法会笼罩父类中的同名办法。如果有办法被@Override标示,但父类中却没有“被@Override标注”的同名办法,则编译器会报错。示例如下:
源码(OverrideTest.java):
View Code
下面是该程序在eclipse中的截图。从中,咱们能够发现“getString()”函数会报错。这是因为“getString() 被@Override所标注,但在OverrideTest的任何父类中都没有定义getString1()函数”。
“将getString() 下面的@Override正文掉”,即可解决该谬误。
2 在反射中应用Annotation
在反射的Class, Method, Field等函数中,有许多于Annotation相干的接口。
这也意味着,咱们能够在反射中解析并应用Annotation。
源码如下(AnnotationTest.java):
package com.skywang.annotation;
import java.lang.annotation.Annotation;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Inherited;
import java.lang.reflect.Method;
/**
* Annotation在反射函数中的应用示例
*
* @author skywang
* @email kuiwu-wang@163.com
*/
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
String[] value() default "unknown";
}
/**
* Person类。它会应用MyAnnotation注解。
*/
class Person {
/**
* empty()办法同时被 "@Deprecated" 和 “@MyAnnotation(value={"a","b"})”所标注
* (01) @Deprecated,意味着empty()办法,不再被倡议应用
* (02) @MyAnnotation, 意味着empty() 办法对应的MyAnnotation的value值是默认值"unknown"
*/
@MyAnnotation
@Deprecated
public void empty(){
System.out.println("\nempty");
}
/**
* sombody() 被 @MyAnnotation(value={"girl","boy"}) 所标注,
* @MyAnnotation(value={"girl","boy"}), 意味着MyAnnotation的value值是{"girl","boy"}
*/
@MyAnnotation(value={"girl","boy"})
public void somebody(String name, int age){
System.out.println("\nsomebody: "+name+", "+age);
}
}
public class AnnotationTest {
public static void main(String[] args) throws Exception {
// 新建Person
Person person = new Person();
// 获取Person的Class实例
Class<Person> c = Person.class;
// 获取 somebody() 办法的Method实例
Method mSomebody = c.getMethod("somebody", new Class[]{String.class, int.class});
// 执行该办法
mSomebody.invoke(person, new Object[]{"lily", 18});
iteratorAnnotations(mSomebody);
// 获取 somebody() 办法的Method实例
Method mEmpty = c.getMethod("empty", new Class[]{});
// 执行该办法
mEmpty.invoke(person, new Object[]{});
iteratorAnnotations(mEmpty);
}
public static void iteratorAnnotations(Method method) {
// 判断 somebody() 办法是否蕴含MyAnnotation注解
if(method.isAnnotationPresent(MyAnnotation.class)){
// 获取该办法的MyAnnotation注解实例
MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
// 获取 myAnnotation的值,并打印进去
String[] values = myAnnotation.value();
for (String str:values)
System.out.printf(str+", ");
System.out.println();
}
// 获取办法上的所有注解,并打印进去
Annotation[] annotations = method.getAnnotations();
for(Annotation annotation : annotations){
System.out.println(annotation);
}
}
}
运行后果:
somebody: lily, 18
girl, boy,
@com.skywang.annotation.MyAnnotation(value=[girl, boy])
empty
unknown,
@com.skywang.annotation.MyAnnotation(value=[unknown])
@java.lang.Deprecated()
3 依据Annotation生成帮忙文档
通过给Annotation注解加上@Documented标签,能使该Annotation标签呈现在javadoc中。
4 可能帮忙查看查看代码
通过@Override, @Deprecated等,咱们能很不便的理解程序的大抵构造。
另外,咱们也能够通过自定义Annotation来实现一些性能。
发表回复