关于java:Java注解学习1Java-Annotation认知包括框架图详细介绍示例说明

17次阅读

共计 11857 个字符,预计需要花费 30 分钟才能阅读完成。

摘要

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 来实现一些性能。

正文完
 0