注解

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)

  

原文地址:https://www.cnblogs.com/yingtoumao/p/8663168.html