Java 注解 @Annotation

作者: 践行者 | 来源:发表于2017-11-01 16:46 被阅读125次

    同时发布于 知乎 Java 注解

    1.注解的由来

    在引入注解之前,在不同类型的应用程序使用XML作为标准的代码配置机制,程序员们描述其代码的形式尚未标准化,每个人的做法各异:transient关键字、注释、接口等,代码和XML的解耦以及未来对这种解耦应用的维护并不低廉,这显然不是一种优雅的方式,随之而来的JDK5.0引入一种崭新的记录元数据的形式——注解被引入到Java中。
    它的作用是修饰编程元素。什么是编程元素呢?例如:包、类、构造方法、方法、成员变量等。

    2.什么是注解

    DK5.0中的类型:1、类(class)2、接口(interface)3、枚举(enum)4、注解(Annotation)
    因此,注解与其他3种类型一样,都可以定义、使用,以及包含有自己的属性、方法

    注解分类

    (1)标记注释:注解的内部没有属性,称作标记注解
    使用方法:@注解名
    使用例子:@MarkAnnotation

    (2)单值注解:注解的内部只有一个属性,称作单值注解
    使用方法:@注解名(属性名=属性值)
    使用例子:@SingleAnnotation(value="abc") //也可以写成@SingleAnnotation("abc")
    *(属性名=属性值)可以简化为(属性值),但是需要满足以下两个条件:
    1、该注解必须为单值注解
    2、该注解的属性名必须为value

    (3)多值注解:注解的内部有多个属性,称作多值注解
    使用方法:@注解名(属性名1=属性值1, 属性名2=属性值2……)
    使用例子:@MultipliedAnnotation(value1 = "abc", value2 = 30……)

    元注解

    Java提供了一下几个元注解(下边会有介绍)

    Target、Retention、Documented和Inherited

    元注解的作用:

    可以用于注解类(annotate Classes)

    可以用于注解接口(annotate Interfaces)

    可以用于注解枚举类型(annotate Enums)

    因此注解同样也可以用于注解注解(annotate Annotations)

    3.注解语法

    3.1注解声明介绍

    我们通过一个简单的例子了解下:

    <pre>
    @Annotation
    @Annotation1(info = "I am Annotation")
    public AnnotationMethod(){
    //代码块
    }
    </pre>

    从代码一步步看:

    01.使用“@”作为前缀声明一盒注解,向编译器说明,该元素(Annotation)是注解

    02.注解后面(),标注它的属性,采用键值对的形式,如果注解只有一个元素(或者只需要指定一个元素的值,其它则使用默认值),也可以表示成: @Annotation("I am Annotation");如果没有元素需要被指定,则不需要括号;

    03.注解用途:可以标注在Java程序的每一个元素上使用:类,域,方法,包,变量等

    3.2 预定义的注解

    3.2.1 @Override

    目的在于标识某一个方法是否覆盖了它的父类的方法

    3.2.2 @Deprecated

    属于标记注解.所谓标记注解,就是在源程序中加入这个标记后,并不影响程序的编译,但有时编译器会显示一些警告信息。

    用于标明已经过时的方法或类

    3.2.3 @SuppressWarnnings

    用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告.

    <pre> <code>

    deprecation:使用了不赞成使用的类或方法时的警告;
    unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型;
    fallthrough:当 Switch 程序块直接通往下一种情况而没有 Break 时的警告;
    path:在类路径、源文件路径等中有不存在的路径时的警告;
    serial:当在可序列化的类上缺少 serialVersionUID 定义时的警告;
    finally:任何 finally 子句不能正常完成时的警告;
    all:关于以上所有情况的警告。

    </code></pre>

    4.定义注解

    4.1 注解的定义

    <pre> <code>
    public @interface CustomAnnotationClass
    </code></pre>

    其中** @interface**说明这是一个自定义注解的定义.

    4.2 定制化

    定制化时,有很多其它属性可以用在自定义注解上,但是 ==目标(Target==)和 ==保留策略(Retention Policy==)是最重要的两个。

    4.2.1 @Target

    用来约束注解可以应用的地方(如方法、类或字段),其中ElementType是枚举类型

    <pre>
    <code>
    public enum ElementType {
    /*标明该注解可以用于类、接口(包括注解类型)或enum声明/
    TYPE,

    /** 标明该注解可以用于字段(域)声明,包括enum实例 */
    FIELD,
    
    /** 标明该注解可以用于方法声明 */
    METHOD,
    
    /** 标明该注解可以用于参数声明 */
    PARAMETER,
    
    /** 标明注解可以用于构造函数声明 */
    CONSTRUCTOR,
    
    /** 标明注解可以用于局部变量声明 */
    LOCAL_VARIABLE,
    

    /** 标明注解可以用于注解声明(应用于另一个注解上)*/
    ANNOTATION_TYPE,

    /** 标明注解可以用于包声明 */
    PACKAGE,
    
    /**
     * 标明注解可以用于类型参数声明(1.8新加入)
     * @since 1.8
     */
    TYPE_PARAMETER,
    
    /**
     * 类型使用声明(1.8新加入)
     * @since 1.8
     */
    TYPE_USE
    

    }
    </code>
    </pre>

    • 当注解未指定Target值时,则此注解可以用于任何元素之上
    • 设定一个值:@Target(ElementType.METHOD)
    • 设定多个值时使用{}包含并用逗号隔开

    <code>@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})</code>

    4.2.2 @Retention

    用来约束注解的生命周期,分别有三个值,源码级别(source),类文件级别(class)或者运行时级别(runtime)

    • SOURCE:注解将被编译器丢弃(该类型的注解信息只会保留在源码里,源码经过编译后,注解信息会被丢弃,不会保留在编译好的class文件里)

    • CLASS:注解在class文件中可用,但会被VM丢弃(该类型的注解信息会保留在源码里和class文件里,在执行的时候,不会加载到虚拟机中),请注意,当注解未定义Retention值时,默认值是CLASS,如Java内置注解,@Override、@Deprecated、@SuppressWarnning等

    • RUNTIME:注解信息将在运行期(JVM)也保留,因此可以通过反射机制读取注解的信息(源码、class文件和执行的时候都有注解的信息),如SpringMvc中的@Controller、@Autowired、@RequestMapping等。

    4.2.3 注解中的细节

    <pre>
    <code>
    @Target(ElementType.TYPE) // 注解在类上
    @Retention(RetentionPolicy.RUNTIME) //保留在运行时
    @interface CustmAnnotationClass {
    /**
    * Java 元注解
    * 1. 目标
    * 2. 保留策略
    */
    String info() default "Annotation";

    }

    @CustmAnnotationClass(info="OOP")
    public class Test {

    }
    </code>
    </pre>

    1. 注解支持以下类型:
    • 所有基本类型
      (int,float,boolean,byte,double,char,long,short)
    • String
    • Class
    • enum
    • Annomation
    • 上述类型的数组形式
    1. 注解可以作为元素的类型,也就是嵌套注解
    2. 注解默认值限制

    元素必须要么具有默认值,要么在使用注解时提供元素的值。

    对于非基本类型的元素,无论是在源代码中声明,还是在注解接口中定义默认值,都不能以null作为值.当然我们可以定义一些特殊的值,例如空字符串或负数,表示某个元素不存在

    4.2.4 注解中的继承
    • 注解不能使用关键字extends来继承某个@interface,但注解在编译后,编译器会自动继承java.lang.annotation.Annotation接口
    • @Inherited

    使用了保留注解@Inherited,但这并不是真的继承,只是通过使用@Inherited,可以让子类Class对象使用getAnnotations()获取父类被@Inherited修饰的注解,换句话说:这个类将自动地把这个注解传递到所有子类中而不用在子类中声明,通常,一个类继承了父类,并不继承父类的注解。这完全和使用注解的目的一致的:提供关于被注解的代码的信息而不修改它们的行为。

    所以:@Inheriated注解仅在存在继承关系的类上产生效果,在接口和实现类上并不工作。
    在默认的情况下,父类的注解并不会被子类继承。如果要继承,就必须加上Inherited注解。

    可以点击这里看看哦

    4.2.5 其他注解属性介绍
    • @Documented 被修饰的注解会生成到javadoc中

    5.注解的获取(反射)

    • 在使用反射之前必须使用import java.lang.reflect.* 来导入和反射相关的类。

    • 如果要得到某一个类或接口的注解信息,可以使用如下代码:

    <pre>
    <code>
    Annotation annotation = TestAnnotation.class.getAnnotation(MyAnnotation.class);
    </code>
    </pre>

    • 如果要得到全部的注解信息可使用如下语句:

    <pre>
    <code>
    Annotation[] annotations = TestAnnotation.class.getAnnotations();

    Annotation[] annotations = TestAnnotation.class.getDeclaredAnnotations();

    </code>
    </pre>

    getDeclaredAnnotations与getAnnotations类似,但它们不同的是getDeclaredAnnotations得到的是当前成员所有的注解,不包括继承的。而getAnnotations得到的是包括继承的所有注解。

    • 如果要得到其它成员的注解,可先得到这个成员,然后再得到相应的注解。如得到myMethod的注解。

    <pre>
    <code>
    Method method = TestAnnotation.class.getMethod("myMethod", null);

    Annotation annotation = method.getAnnotation(MyAnnotation.class);
    </code>
    </pre>
    注:要想使用反射得到注解信息,这个注解必须使用

    @Retention(RetentionPolicy.RUNTIME)进行注解。

    • 判断指定类型的注解是否存在于此元素

    <pre>
    <code>
    isAnnotationPresent(Class<? extends Annotation> annotationClass)
    </code>
    </pre>

    6. Java8 变化

    • @Repeatable
      表示在同一个位置重复相同的注解
      <code>@Repeatable(MyAnnotation.class)</code>
    • 新增ElementType

    TYPE_PARAMETER 和 TYPE_USE ,在Java8前注解只能标注在一个声明(如字段、类、方法)上,Java8后,新增的TYPE_PARAMETER可以用于标注类型参数,而TYPE_USE则可以用于标注任意类型(不包括class)。

    7.自动测试机的写法

    7.1自动测试机的原理:

    使用Annotation来Annotate元素的实质是:每一个ElementType内部的元素都有两个方法,分别为
    (注:为方便理解,以下使用的TestCase为某个特定的自定义注释)

    (1)isAnnotationPresent(TestCase.class) //判断该元素是否被TestCase所注释

    (2)getAnnotation(TestCase.class) //获得TestCase的类对象

    7.2自动测试机的工作过程是:

    (1)首先通过反射,获得被测类o中的每一个方法

    (2)对每一个方法通过使用isAnnotationPresent(TestCase.class)判断其是否被TestCase所注释(注意是.class!)

    (3)如果某方法method被TestCase所注释,则通过method的getAnnotation(TestCase.class)获得TestCase的类对象tc

    (4)通过tc的value()方法,获得该类对象的属性value

    (注:此处使用的value()方法,正是在TestCase中定义的value属性,再次理解在注释中定义的value既是属性,也是方法)

    (5)调用method方法的invoke(o,value),用value对method进行测试

    参考福利

    官方Java注解地址:http://docs.oracle.com/javase/tutorial/java/annotations/

    维基百科中关于Java注解的解释:http://en.wikipedia.org/wiki/Java_annotation

    Java规范请求250:http://en.wikipedia.org/wiki/JSR_250

    Oracle 注解白皮书:http://www.oracle.com/technetwork/articles/hunter-meta-096020.html

    注解API:http://docs.oracle.com/javase/7/docs/api/java/lang/annotation/package-summary.html

    相关文章

      网友评论

        本文标题:Java 注解 @Annotation

        本文链接:https://www.haomeiwen.com/subject/euegpxtx.html