摘要
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 @Inherited21 @interface Inheritable22 {23 }24 25 @Inheritable26 class InheritableFather27 {28 public InheritableFather() {29 // InheritableBase是否具备 Inheritable Annotation30 System.out.println("InheritableFather:"+InheritableFather.class.isAnnotationPresent(Inheritable.class));31 }32 }33 34 /**35 * InheritableSon 类只是继承于 InheritableFather,36 */37 public class InheritableSon extends InheritableFather38 {39 public InheritableSon() {40 super(); // 调用父类的构造函数41 // InheritableSon类是否具备 Inheritable Annotation42 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来实现一些性能。