zoukankan      html  css  js  c++  java
  • 注解

    1、注解(Annotation)的概念和作用:

      从 JDK5 开始,java 增加了对注解的支持。注解是代码中的特殊标记,可以在编译、类加载、运行时被读取,并执行相应的处理。

      通过使用注解,开发人员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息,代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或部署。

      注解就像修饰符一样,可以用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,这些信息被存储在注解的 “name=value” 对中。

      注解并不会影响程序代码的执行,无论增加或删除注解,代码都始终如一的执行,如果希望程序中的注解在运行时起一定的作用,只有通过某种配套的工具对注解中的信息进行访问和处理,访问和处理注解的工具统称 APT(Annotation Precessing Tool)。

      

    2、java中的常用注解:

      基本注解(定义在 java.lang 包中)

      @Override : 限定重写父类(包括实现的接口)方法,用于强制子类必须覆盖父类的方法。

        告诉编译器检查被修饰的方法,保证父类要包含一个被该方法重写的方法,否则就会编译出错,帮助程序员避免方法名拼写错误而导致bug。

        @Override 只能用于修饰方法,不能修饰其他程序元素。

     

      @Deprecated : 标记已过时。

        用于表示某个程序元素已过时,当其他程序员使用被标记为已过时的程序元素时,编译器将会给出警告。

        @Deprecated 可用于修饰类、接口、方法、变量。

     

      @SuppressWarnings : 抑制编译器警告。

        表示被修饰的程序元素(以及该程序元素中的所有子元素)取消显示指定的编译器警告。

        通过 value 属性设置指定的编译器警告,如: @SuppressWarnings(value=“unchecked”)

      

      @SafeVarargs(java7 中新增):专门为抑制 ”堆污染“ 警告提供的。

        @SafeVarargs 可用于修饰方法、构造器。

     

      @FunctionalInterface(java8 中新增):指定某个接口必须是函数式接口(如果接口中只有一个抽象方法(可以包含多个默认方法或多个静态方法),则该接口就是函数式接口)。

        用于告诉编译器检查修饰的接口,保证该接口只能包含一个抽象方法,否则就会编译出错。

         @FunctionalInterface 只能修饰接口,不能修饰其他程序元素。

      元注解(定义在 java.lang.annotation 包中)

      @Retention :指定被修饰的注解可以保留多长时间。

        @Retention 包含一个 RetentionPolicy 类型的 value 成员变量,使用该注解时,必须为该 value 成员变量指定值,value 成员变量的值:

           (1)RetentionPolicy.CLASS : 编译器把被修饰的注解记录在 class 文件中,当运行程序时,JVM 不可获取注解信息,这是默认值。

           (2)RetentionPolicy.RUNTIME : 编译器把被修饰的注解记录在 class 文件中,当程序运行时,JVM 可获取注解信息,程序可以通过反射获取该注解信息。

           (3)RetentionPolicy.SOURCE : 被修饰的注解只保留在源代码中,编译器直接丢弃这种注解。

            综上可知,如果需要通过反射获取注解信息,就必须使用 value 属性值为 RetentionPolicy.RUNTIME 的 @Retention。

        @Retention 只能用于修饰注解定义。

          

      @Target : 指定被修饰的注解可用于修饰哪些程序元素。

        @Target 包含一个 ElementType 类型的 value 成员变量,使用该注解时,必须为该 value 成员变量指定值,value 成员变量的值:

          (1)ElementType.ANNOTATION_TYPE : 指定被修饰的注解只能修饰注解定义。

          (2)ElementType.CONSTRUCTOR : 指定被修饰的注解只能修饰构造器。

          (3)ElementType.FIELD : 指定被修饰的注解只能修饰成员变量。

          (4)ElementType.LOCAL_VARIABLE : 指定被修饰的注解只能修饰局部变量。

          (5)ElementType.METHOD : 指定被修饰的注解只能修饰方法。

          (6)ElementType.PACKAGE : 指定被修饰的注解只能修饰包。

          (7)ElementType.PARAMETER : 指定被修饰的注解只能修饰参数。

          (8)ElementType.TYPE : 指定被修饰的注解可以修饰类、接口(包括注解类型)、枚举定义等类型声明。

          (9)ElementType.TYPE_PARAMETER :指定被修饰的注解只能修饰类型参数。

          (10)ElementType.TYPE_USE :指定为这种类型的注解,被称为类型注解,类型注解可以用在任何用到类型的地方。

        @Target 只能用于修饰注解定义。

        

      @Documented :指定被修饰的定义注解类将被 javadoc 工具提取成文档。

        如果定义注解类时使用了 @Documented 修饰,则所有被定义注解所修饰的程序元素的 API 文档中都将会包含定义注解的说明。

        @Documented 只能用于修饰注解定义。

     

      @Inherited :表示被修饰的定义注解具有继承性。

        如果某个类使用了定义注解修饰,则被修饰类的子类也将自动被定义注解修饰。

     

      @Repeatable :表示被修饰的定义注解是一个重复注解(java8新增)。

        java8 以前,同一个程序元素前最多只能使用一个相同类型的注解,如果需要在同一个程序元素前使用多个同类型的注解,则必须使用注解容器。例如:

    @MyTags({@MyTag(name = "lisi", age = 22),
            @MyTag(name = "zhangsan", age = 33)})
    public class TestDemo {
    
    }

      

        java8 新增了一个 @Repeatable 元注解,用于定义重复注解。

        使用 @Repeatable 修饰定义注解时,必须为它的 value 成员变量指定值,该成员变量的值是一个容器注解,该容器注解可以包含多个定义注解,因此还需要定义一个容器注解。

        注:容器注解的保留期必须大于或等于它所包含的注解的保留期,否则编译器会报错。这是因为我们需要通过容器注解来获取被包含的注解。

         定义重复注解的示例:

    // MyTag.class 文件:

    @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Repeatable(MyTags.
    class) public @interface MyTag { String name(); int age(); }
    // MyTags.class 文件:

    @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE)
    public @interface MyTags { MyTag[] value(); }
    // TestDemo.class 文件:

    @MyTag(name = "lisi", age = 22) @MyTag(name = "zhangsan", age = 33) public class TestDemo { public static void main(String[] args) { MyTag[] tags = TestDemo.class.getDeclaredAnnotationsByType(MyTag.class); for (MyTag tag : tags) { System.out.println(tag.name() + " : " + tag.age()); } /* * 上面输出: * lisi : 22 * zhangsan : 33 * */ MyTags container = TestDemo.class.getDeclaredAnnotation(MyTags.class); System.out.println(container); /* * 上面输出: * @com.test.repetition.MyTags(value=[@com.test.repetition.MyTag(name=lisi, age=22), @com.test.repetition.MyTag(name=zhangsan, age=33)]) * */ MyTag tag = TestDemo.class.getDeclaredAnnotation(MyTag.class); System.out.println(tag); /* * 上面输出: * null * */ } }

     

    3、自定义注解:

      定义新的注解类型使用关键字 @interface ,如下代码可定义一个简单的注解类型:

    public @interface Test {
    }

      

      注解中的成员变量,以无形参的方法形式来声明,其方法名和返回值定义了该成员变量的名字和类型,如下,定义一个成员变量:

    public @interface Test {
        //定义两个成员变量
        // 名字为 name ,类型为 String
        String name();
        // 名字为 age , 类型为 int
        int age();
    }

      

      可以在定义注解的成员变量时为其指定初始值(默认值),指定成员变量的初始默认值,使用关键字 default ,如:

    public @interface Test {
        //定义两个成员变量
        // 名字为 name ,类型为 String
        String name() default "lisi";
        // 名字为 age , 类型为 int
        int age() default 21;
    }

     

      一旦在注解中定义了成员变量后,使用该注解时,就应该为注解的成员变量指定值(注解定义时没有为成员变量指定默认值),例如:

    public class TestAnnotation {
    
        // 使用带成员变量的注解时,需要为成员变量赋值
        @Test(name = "zhangsan", age = 22)
        public void info() {
            
        }
    }

      

      如果定义注解时,为注解的成员变量指定了默认值,则使用该注解时,可以不为有默认值的成员变量赋值,如:

    public @interface Test {
        //定义两个成员变量
        // 名字为 name ,类型为 String
        String name() default "lisi";                          
        // 名字为 age , 类型为 int
        int age() default 21;
    }
    // 可以不为成员变量赋值,此时成员变量使用默认值。 也可以为成员变量赋值,赋值后,成员变量的默认值将不会起作用
    @Test
    public void info() { }

      

      根据注解是否可以包含成员变量,可以把注解分为如下两类:

        标记注解:没有包含成员变量的注解,被称为标记注解。这种注解利用自身的存在与否来提供信息,如 @Override 。

        元数据注解:包含成员变量的注解,可以接收更多的元数据。

      

    4、提取注解信息:

      使用注解修饰了类、方法、成员变量等程序元素后,这些注解并不会自己生效,必须由开发者提供相应的工具来提取并处理注解信息。

      java 中,使用 Annotation 接口来代表程序元素前面的注解,该接口是所有注解的父接口。

      java中,使用 java.lang.reflect 包下的 AnnotatedElement 接口来代表程序中可以接收注解的程序元素,该接口主要有如下实现类:

        Class :类定义

        Constructor :类的构造器定义

        Field: 类的成员变量定义

        Method :类的方法定义

        Package:类的包定义

      

      只有在定义注解时使用了 @Retention(RetentionPolicy.RUNTIME) 修饰,该注解在运行时才可见,JVM 才会在装载 class 文件时,读取保存在 class 文件中的注解。

      

      AnnotatedElement 接口定义了如下方法,用来访问注解的信息:   

      // 返回该程序元素上存在的、指定类型的注解,如果该类型的注解不存在,则返回null
      <T extends Annotation> T getAnnotation(Class<T> annotationClass)


      // java8 新增方法,该方法尝试获取直接修饰该程序元素、指定类型的注解,如果该类型的注解不存在,则返回null
      <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass)


      // 返回该程序元素上存在的所有注解
      Annotation[] getAnnotations()


      // 返回直接修饰该程序元素的所有注解
      Annotation[] getDeclaredAnnotations()


      // 判断该程序元素上是否存在指定类型的注解
      boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)


      // 获取修饰该程序元素、指定类型的多个注解
      <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass)


      // 获取直接修饰该程序元素、指定类型的多个注解
      <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass)

      

  • 相关阅读:
    【问题记录】ajax dataType属性
    【问题记录】springMVC @Valid使用不生效问题
    Initialization of bean failed; nested exception is java.lang.NoClassDefFoundError: javax/jms/JMSContext
    mysql优化:explain 和 profile
    【问题记录】mysql TIMEDIFF 和 TIMESTAMPDIFF的使用
    初次搭建spring boot 项目(实验楼-学习笔记)
    JqGrid自定义toolbar
    MS SQL SERVER 2008 R2 实例服务启动出现10048错误解决办法
    C#快速导入海量XML数据至SQL Server数据库
    SQL2012之FileTable与C#的联合应用
  • 原文地址:https://www.cnblogs.com/yingtoumao/p/8663168.html
Copyright © 2011-2022 走看看