面试必问系列之----Java中的注解(Annotation)详解(二)

227 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第六天,点击查看活动详情 

注解(Annotation)

前言回顾

我们在注解一种已经了解了注解分为两种注解,标准注解(内置注解)和元注解,标准注解(内置注解)我们在上一篇已经了解,本篇我们将来了解元注解

  1. 元注解
    除了标准注解就是元注解,它用来注解其他注解,从而创建新的注解。元注解有以下几种
    • @Target
      注解所修饰的对象范围
    • @Retention
      用来申明注解的保留策略
    • @Documented
      表示这个注解应该被JavaDoc这个工具记录
    • @Inherited
      表示注解可以被继承
    • @Repeatable JDk8新增的元注解,允许一个注解在同一个声明类型(类、属性或者方法)上多次使用

@Target

用于描述注解的范围,即注解能在哪里使用。他说明了注解所修饰的对象范围, packages、types(类、接口、枚举、注解类)、类成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)等。这样定义注解类时使用@Target我们能够清楚地指导注解的使用范围,我们用ElementType类型的数组来定义范围,其中有以下几种取值,对应的不同对象范围

  • ElementType.TYPE
    能修饰类、接口或者枚举类型
  • ElementType.FIELD
    能修饰成员变量(包括枚举常量)
  • ElementType.METHOD
    能修饰方法
  • ElementType.PARAMETER
    能修饰参数
  • ElementType.CONSTRUCTOR
    能修饰构造方法
  • ElementType.LOCAL_VARIABLE
    能修饰局部变量
  • ElementType.ANNOTATION_TYPE
    能修饰注解
  • ElementType.PACKAGE
    能修饰包
  • ElementType.TYPE_PARAMETER
    类型参数声明
  • ElementType. TYPE_USE
    使用类型

@Retention

用于描述注解保留策略,即注解的生命周期,保留的时间。
这个保留策略有三种类型,用RetentionPolicy来表示范围

  • RetentionPolicy.SOURCE
    源码级注解。注解信息只会保留在.java源码中,源码在编译后,注解信息会被丢弃,不会保留在.class中
  • RetentionPolicy.CLASS 编译时注解。注解信息回保留在.java源码以及.class中。当运行Java程序时,JVM会丢弃该注解的信息,不会保留在JVM中
  • RetentionPolicy.RUNTIME
    运行时注解。当运行Java程序时,JVM也会保留该注解信息,可以通过反射获取该注解信息。

学习了@Target@Retention那么接下来我们就以@Verride为例,看看如何使用@Target@Retention

@Target(ElementType.METHOD)//表示该注解用于修饰方法
@Retention(RetentionPolicy.SOURCE)//表示只在源代码中保留
public @interface Override{

}

解析: @interface是定义接口的关键字,有点类似interface,请不要搞错哦。

@Documented

@Documented 是一个标记注解,没有成员变量。用 @Documented 注解修饰的注解类会被 JavaDoc 工具提取成文档。默认情况下,JavaDoc 是不包括注解的,但如果声明注解时指定了 @Documented,就会被 JavaDoc 之类的工具处理,所以注解类型信息就会被包括在生成的帮助文档中。
接下来我们来看一下实例:

@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface TestDocumented{
    public String value() default "@Documented test";
}

//接下来我们来试用一下
@TestDocumented
public class DocumentedTest{

}

接下来我们用这个注解来生成一下文档,步骤如下

1659792730413.jpg

1659792739494.jpg 到这里我们就生成了HTML格式的文档,那我们来看看 5-191230145036105.png

5-191230145403933.png

@Inherited

@Inherited作用是,使用此注解声明出来的自定义注解,在使用此自定义注解时,如果注解在类上面时,子类会自动继承此注解,否则的话,子类不会继承此注解。这里一定要记住,使用Inherited声明出来的注解,只有在类上使用时才会有效,对方法,属性等其他无效。

简单点:使用该注解的注解父类的子类可以继承父类的注解。
我们来看一下例子:

//定义一个注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface InheritedTest {
    String value();
}
@InheritedTest("拥有Inherited")
public class Person {
  public void method(){
  }
  public void method2(){
  }
}
//继承一个有@Inherited注解的类
public class Student extends Person {
}
public class TestInherited {
  public static void main(String[] args) {
    //我们用反射来拿到对象
    Class<Student> studentClass = Student.class;
    //检查此类中是否存InheritedTest注释类型的注释
    if (studentClass.isAnnotationPresent(InheritedTest.class)){
      //输出注解的值
      System.out.println(studentClass.getAnnotation(InheritedTest.class).value());
    }
  }
}

输出结果:
拥有Inherited

我们发现注解被继承了
那我们来看看不使用@Inherited注解的情况:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface IsNotInherited {
    String value();
}
@IsNotInherited("未拥有Inherited")
public class Person {
  public void method(){
  }
  public void method2(){
  }
}
public class Student extends Person {
}
public class TestInherited {
  public static void main(String[] args) {
      Class<Student> studentClass = Student.class;
      if (studentClass.isAnnotationPresent(IsNotInherited.class)){
          System.out.println(studentClass.getAnnotation(IsNotInherited.class).value());
      }
  }
}
输出:
(没有输出)

@Repeatable

@Repeatable是java8为了解决同一个注解不能重复在同一类、方法、属性上使用的问题。
我们来看一下列子:

@Repeatable(Schedules.class)
public @interface Schedule {
    String dayOfMonth() default "first";
    String dayOfWeek() default "Mon";
    int hour() default 12;
}
@Retention(RetentionPolicy.RUNTIME)
public @interface Schedules {
    Schedule[] value();
}
@Schedule(dayOfMonth="last")
@Schedule(dayOfWeek="Wed", hour=24)
public class RepetableAnnotation{

    @Schedule(dayOfMonth="last")
    @Schedule(dayOfWeek="Fri", hour=23)
    public void doPeriodicCleanup(){}

    public static void main(String[] args) throws NoSuchMethodException {

        Method doPeriodicCleanup = RepetableAnnotation.class.getMethod("doPeriodicCleanup");

        Schedules schedules = doPeriodicCleanup.getAnnotation(Schedules.class);
        System.out.println("获取标记方法上的重复注解:");
        for (Schedule schedule: schedules.value()){
            System.out.println(schedule);
        }

        System.out.println("获取标记类上的重复注解:");
        if (RepetableAnnotation.class.isAnnotationPresent(Schedules.class)){
            schedules = RepetableAnnotation.class.getAnnotation(Schedules.class);
            for (Schedule schedule: schedules.value()){
                System.out.println(schedule);
            }
        }

    }
}

这一篇就结束了,下一篇我们开自定义注解