2021.5.26:注解Annotation

1、注解Annotation

什么是注解?注解是放在Java源码的类、方法、字段、参数前的一种特殊的注释

@Resource("hello")
public class Hello{
    @Inject
    int n;
    @PostConstruct
    public void hello(@Param String name){
        System.out.println(name);
    }
    @Override
    public String toString(){
        return "Hello";
    }
}

注释会被编译器直接忽略注解则可以被编译器打包进入class文件,因此,注解是一种用作标注的“元数据”。

2、注解的作用

从JVM的角度来看,注解本身对代码逻辑没有任何影响,如何使用注解完全由工具决定

Java的注解可以分为三类:

2.1、编译器使用的注解

  • @Override:让编译器检查该方法是否正确地实现了覆写
  • @SuppressWarnings:告诉编译器忽略此处代码产生的警告

这类注解不会被编译进.class文件,它们在编译后就被编译器扔掉了

2.2、由工具处理.class文件使用的注解

比如有些工具会加载class的时候,对class做动态修改,实现一些特殊的功能。

这类注解会被编译进.class文件,但加载结束后并不会存在于内存中。这类注解只被一些底层库使用,一般我们不必自己处理

2.3、程序运行期间能够读取的注解

这类注解在加载后一直存在于JVM中,这也是最常用的注解。例如,一个配置了@PostConstruct的方法会在调用构造方法后自动被调用(这是Java代码读取该注解实现的功能JVM并不会识别该注解)。

定义一个注解时,还可以配置参数。配置参数包括:

  • 所有基本类型;
  • String;
  • 枚举类型;
  • 以上三种类型以及Class的数组

因为配置参数必须是常量,因此,上述限制保证了在定义注解时就已经确定了每个参数的值

注解的配置参数可以有默认值,缺少某个配置参数时将使用默认值。

此外,大部分注解会有一个名为value的配置参数,对此参数赋值,可以只写常量不写参数名value,相当于省略了value参数

如果只写注解,说明全部使用默认值

public class Main{
    @Check(min=0,max=100,value=55)
    public int n;

    @Check(value=99)
    public int p;

    @Check(99)//相当于@Check(value=99)
    public int x;
    
    @Check
    public int y;
}

@Check就是一个注解。以上4个@Check的解释

  • @Check(min=0,max=100,value=55)明确定义了三个参数;
  • @Check(value=99)只定义了一个value参数,它实际上是和@Check(99)完全一样的;
  • @Check表示所有参数都使用默认值。

 

3、定义注解

Java使用@interface语法来定义注解,其格式如下:

public @interface Report{
    int type() default 0;
    String level() default "info";
    String value() default "";
}

注解的参数类似无参数方法,可以用default设定一个默认值(强烈推荐)。

最常用的参数应当命名为value

4、元注解(meta annotation)

元注解:用来修饰其他注解注解

Java标准库已经定义了一些元注解,我们只需要使用它们不需要编写它们。

4.1、@Target

@Target是最常用的元注解。

@Target可以用于以下地方:

  • 类或接口:ElementType.TYPE
  • 字段:ElementType.FIELD
  • 方法:ElementType.METHOD
  • 构造方法:ElementType.CONSTRUCTOR
  • 方法参数:ElementType.PARAMETER

例如,如果我们想要把注解@Report用于方法上,必须添加一个@Target(ElementType.METHOD)

@Target(ElementType.METHOD)//表明以下定义的注解将被用于方法上
public @interface Report{
    int type() default 0;
    String level() default "info";
    String value() default "";
}

如果我们想把注解@Report用在方法字段(也就是说该注解将被用于多个地方),可以把注解参数变为数组{ElementType.METHOD,ElementType.FIELD}

@Target({
    ElementType.METHOD,
    ElementType.FIELD
})//将应用的地方用数组标识
public @interface Report {
    ...
}

实际上@Target定义的valueElementType[ ]数组,只有一个元素时,可以省略数组的写法

4.2、@Retention

元注解@Retention定义了Annotation的生命周期

  • 仅编译期:RetentionPolicy.SOURCE
  • 仅class文件:RetentionPolicy.CLASS
  • 运行期:RetentionPolicy.RUNTIME

如果@Retention不存在,则该Annotation默认为CLASS

因为通常我们自定义的Annotation都是RUNTIME,所以,必须要加上@Retention(RetentionPolicy.RUNTIME)这个元注解:

@Retention(RetentionPolicy.RUNTIME)//RUNTIME时起作用的注解
public @interface Report{
    int type() default 0;
    String level() default "info";
    String value() default "";
}

4.3、@Repeatable

使用@Repeatable这个元注解可以定义Annotation是否重复。这个注解应用并不是很广泛。

某个元注解如果经@Repeatable修饰后,就可以在某个类型声明处,添加多个该元注解

4.4、@Inherited

使用@Inherited定义子类是否可以继承父类的Annotation@Inherit仅仅针对@Target(ElementType.TYPE类型的Annotation有效,并且仅仅针对class继承,对interface的继承无效:

@Inherited
@Target(ElementType.TYPE)
public @interface Report{
    int type() default 0;
    String level() default "info";
    String value() default "";
}

在使用的时候,如果一个class用到了@Report

@Report(type=1)
public class Person{}

则它的子类默认也定义了该注解:

public class Student extends Person{}

5、如何定义Annotation

我们总结一下定义Annotation的步骤:

1、用@interface定义注解,也就是说,注解的数据类型@interface

public @interface Report{}

2、添加参数默认值

public @interface Report{
    int type() default 0;
    String level() default "info";
    String value() default "";
}

最常用的参数定义为value(),推荐所有的参数都尽量设置默认值。

3、用元注解配置注解

指定注解的生命周期、起作用的位置

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Report{
    int type() default 0;
    String level() default "info";
    String value() default "";
}

其中,必须设置@Target@Retention,其中@Retention一般设置为RUNTIME,因为我们自定义的注解通常要求在运行期间读取,一般情况下,不必写@Inherited@Repeatable

总结

1、注解(Annotation)——放在class、method、field、parameter前的一种特殊的注释,可以认为是一种用作标注元数据

2、普通注释(用//说明)会被编译器直接忽略注解会被打包进class文件

3、注解本身代码逻辑没有影响,如何使用注解工具决定

4、注解有3类:

  • 编译器使用(@Override、@SuppressWarnings
  • .class文件使用,对class做动态修改(不常用,用于底层库)
  • 运行期(Runtime)读取的注解——说明某些功能实现的时机(最常用,由Java代码读取并实现,但JVM不会识别)

5、注解的配置参数类型:

  • 基本类型;
  • String;
  • 枚举类型;
  • 以上三种类型以及Class数组;

6、配置参数必须是常量,定义注解时必须指定每个参数的值

7、配置参数可以有默认值,缺少某个参数值时将使用默认值;

8、大部分注解都有名为value的配置参数,对它赋值,可以不写参数名,只写参数值

    @Check(min=0,max=100,value=55)
    public int n;

    @Check(value=99)
    public int p;

    @Check(99)//相当于@Check(value=99)
    public int x;
    
    @Check
    public int y;

9、定义注解——@interface语法

public @interface XXX{
    int type() default 0;
    String level() default "info";
    String value() default "";
}

注解的参数类似无参方法(如上文的type()、level()、value()都是注解参数),推荐用default为这些参数设置默认值最常用的参数value

10、元注解(Meta Annotation)——修饰注解注解

11、我们并不需要编写元注解,Java标准库已经定义好了

  • @Target——定义注解用在何处;
  • @Retention——定义注解的生命周期;
  • @Repeatable——定义注解可否重复;
  • @Inherited——定义子类可否继承父类的注解;

后两种元注解不常用

12、对9的补充,定义注解时使用元注解

@Target(ElementType.TYPE)//该注解用在类或接口上
@Retention(RetentionPolicy.RUNTIME)//该注解是运行期注解,只在运行期间读取
public @interface Report{
    int type() default 0;
    String level() default "info";
    String value() default "";
}

 

posted @ 2021-05-29 15:20  ShineLe  阅读(45)  评论(0)    收藏  举报