厦门市建设厅网站,html查看器,软装设计公司网站,网站建设鑫科技摘要 Java Annotation是JDK5.0引入的一种注释机制。 网上很多关于Java Annotation的文章#xff0c;看得人眼花缭乱。Java Annotation本来很简单的#xff0c;结果说的人没说清楚#xff1b;弄的看的人更加迷糊。 我按照自己的思路#xff0c;对Annotation进行了整理。理解… 摘要 Java Annotation是JDK5.0引入的一种注释机制。 网上很多关于Java Annotation的文章看得人眼花缭乱。Java Annotation本来很简单的结果说的人没说清楚弄的看的人更加迷糊。 我按照自己的思路对Annotation进行了整理。理解 Annotation 的关键是理解Annotation的语法和用法对这些内容我都进行了详细说明理解Annotation的语法和用法之后再看Annotation的框架图可能有更深刻体会。废话就说这么多下面开始对Annotation进行说明。若您发现文章中存在错误或不足的地方希望您能指出 第1部分 Annotation架构 先看看Annotation的架构图 从中我们可以看出 (01) 1个Annotation 和 1个RetentionPolicy关联。 可以理解为每1个Annotation对象都会有唯一的RetentionPolicy属性。 (02) 1个Annotation 和 1~n个ElementType关联。 可以理解为对于每1个Annotation对象可以有若干个ElementType属性。 (03) 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”关联并且与 “1n个ElementType”关联。可以通俗的理解为每1个Annotation对象都会有唯一的RetentionPolicy属性至于ElementType属性则有1~n个。 (02) ElementType 是Enum枚举类型它用来指定Annotation的类型。 “每1个Annotation” 都与 “1n个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”关联并且与 “1n个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用来声明AnnotationDocumented用来表示该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 skywang5 * email kuiwu-wang163.com6 */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) 1 /**2 * Inherited 演示示例3 * 4 * author skywang5 * email kuiwu-wang163.com6 */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: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) 1 package com.skywang.annotation;2 3 import java.util.Date;4 5 public class SuppressWarningTest {6 7 //SuppressWarnings(value{deprecation})8 public static void doSomething(){9 Date date new Date(113, 8, 26);
10 System.out.println(date);
11 }
12
13 public static void main(String[] args) {
14 doSomething();
15 }
16 } 说明 (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-wang163.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*/MyAnnotationDeprecatedpublic 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 {// 新建PersonPerson person new Person();// 获取Person的Class实例ClassPerson 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来实现一些功能。