注解(Annotation)

一、注解概述

从 JDK 5 开始,Java增加了对元数据(MetaData)的支持,也就死Annotation(即注解)。注解其实是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用注解,开发人员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。

注解提供了一种为程序元素设置元数据的方法,注解就像修饰符一样,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,这些信息被存储在注解的“name = value”中。

二、注解的作用

  • 编写文档: 通过代码里标识的元数据生成文档【生成文档doc文档】
  • 代码分析: 通过代码里标识的元数据对代码进行分析【使用反射】
  • 编译检查: 通过代码里标识的元数据让编译器能够实现基本的编译检查【Override等】

编写文档

首先,我们要知道Java中是有三种注释的,分别为单行注释、多行注释和文档注释。而文档注释中,也有@开头的元注解,这就是基于文档注释的注解。我们可以使用javadoc命令来生成doc文档,此时我们文档的内元注解也会生成对应的文档内容。这就是编写文档的作用。

代码分析

我们频繁使用之一,也是包括使用反射来通过代码里标识的元数据对代码进行分析的,此内容我们在后续展开讲解。

编译检查

至于在编译期间在代码中标识的注解,可以用来做特定的编译检查,它可以在编译期间就检查出“你是否按规定办事”,如果不按照注解规定办事的话,就会在编译期间飘红报错,并予以提示信息。可以就可以为我们代码提供了一种规范制约,避免我们后续在代码中处理太多的代码以及功能的规范。比如,@Override注解是在我们覆盖父类(父接口)方法时出现的,这证明我们覆盖方法是继承于父类(父接口)的方法,如果该方法稍加改变就会报错;@FunctionInterface注解是在编译期检查是否是函数式接口的,如果不遵循它的规范,同样也会报错。

三、常见基本注解

  • @Override: 标记在成员方法上,用于标识当前方法是重写父类(父接口)方法,编译器在对该方法进行编译时会检查是否符合重写规则,如果不符合,编译报错。
  • @Deprecated: 用于标记当前类、成员变量、成员方法或者构造方法过时如果开发者调用了被标记为过时的方法,编译器在编译期进行警告。
  • @SuppressWarnings: 压制警告注解,可放置在类和方法上,该注解的作用是阻止编译器发出某些警告信息。

四、元注解

元注解是修饰其他注解的注解。

常用的4个元注解:@Retention、@Target、@Document、@Inherit

@Retention

@Retention只能用于修饰注解定义,用于指定被修饰的注解可以保留多长时间

@Retention包含一个RetentionPolicy类型的value成员变量,所以使用@Retention时必须为该value成员变量指定值,value的值只能是如下三个:

  • RetentionPolicy.CLASS编译器将把注解记录在class文件中。当运行Java程序时,JVM不可获取注解信息。(默认)。
  • RetentionPolicy.RUNTIME编译器将把注解记录在class文件中。当运行Java程序时,JVM可以获取注解信息,程序可以通过反射获取该注解信息。
  • RetentionPolicy.SOURCE注解只保留在源代码中,编译器直接丢弃这种注解。

如果需要通过反射获取注解信息,需要将value属性值设置为RetentionPolicy.RUNTIME

@Target

@Target也只能修饰注解定义,它用于指定被修饰的注解能用于哪些程序单元。

@Target元注解也包含一个value成员变量,value的值只能是如下几个:

  • ElementType.ANNOTATION_TYPE:修饰注解。
  • ElementType.CONSTRUCTOR:修饰构造器。
  • ElementType.FIELD:修饰成员变量。
  • ElementType.LOCAL_VARIABLE:修饰局部变量。
  • ElementType.METHOD:修饰方法。
  • ElementType.PACKAGE:修饰包。
  • ElementType.PARAMETER:修饰参数。
  • ElementType.TYPE:修饰类、接口(包括注解类型)、枚举。

@Document

@Document用于指定被元注解修饰的注解类将被javadoc工具提取成文档。

自定义一个TestDocumentAnnotation注解:

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface TestDocumentAnnotation {
}

编写一个TestDocument测试类:

public class TestDocument {
    @TestDocumentAnnotation
    public void info() {
        System.out.println("TestDocumentAnnotation");
    }
}

生成的JavaDoc:

image-20230722210910080

@Inherited

@Inherited元注解指定被它修饰的注解将具有继承性--------如果某个类使用了@Xxx注解(定义该注解时使用了@Inherited修饰)修饰,则其子类将自动被@Xxx修饰。

五、自定义注解

定义注解

自定义注解使用关键字@interface

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME) // 使用@Retention指定注解保留到运行时
@Target(ElementType.METHOD) // @Target指定注解可用于修饰方法
public @interface MyAnnotation {
    // 注解中的成员变量以方法的形式来定义
    // default为两个成员变量指定默认初始值
    String var1() default "gengduc";
    int var2() default 21;
}

如果为注解的成员变量指定了默认值,使用该注解时则可以不为这些成员变量指定值,而是直接使用默认值。

提取注解信息

public class MyClass {
    @MyAnnotation
    public void info1() {
        System.out.println("info1...");
    }

    @MyAnnotation(var1 = "CGengdu", var2 = 22)
    public void info2() {
        System.out.println("info2...");
    }
}
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

public class TestMyAnnotation {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> clazz = Class.forName("MyClass");
        Method[] methods = clazz.getMethods();

        for (Method method : methods) {
            System.out.println("Method = " + method);
            Annotation[] annotations = method.getAnnotations();
            for (Annotation annotation : annotations) {
                System.out.println("\t Annotation = " + annotation);
            }
        }
    }
}

Method = public void MyClass.info1()
Annotation = @MyAnnotation(var2=21, var1=gengduc)
Method = public void MyClass.info2()
Annotation = @MyAnnotation(var2=22, var1=CGengdu)

参考链接

posted @ 2023-07-22 22:29  gengduc  阅读(19)  评论(0编辑  收藏  举报  来源