8、注解

什么是注解

注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。

它作用于程序元素(类、字段、方法、局部变量、方法参数等)的上面,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。

与注释的区别

功能

  1. 生成文档这是最常见的,也是java最早提供的注解,常用的有@param@return

  2. 在编译时进行格式检查,如@Override放在方法上,如果这个方法并不是覆盖了超类方法,则编译时就能检查出

  3. 跟踪代码依赖性,实现替代配置文件功能,比较常见的是spring2.5开始的基于注解配置,作用就是减少配置

  4. 在反射的Class,Method,Field等类的实例函数中,有许多于 Annotation 相关的接口,可以在反射中解析并使用 Annotation。

使用注解

@AnnotationName
public void show() {}

@AnnotationName
int i;

@AnnotationName
class user {
    public void show() {}
}

注解的分类

预定义注解

JDK定义好的注解,常用的有:

自定义注解

注解只有属性(抽象方法),没有方法体

public @interface 注解名{
    public String 属性名();
    public String 属性名() default 默认值;
    //由于注解的本质是接口,所以权限修饰符可以省略,默认是public
    String 属性名() default 默认值;
}

注解的实质

public interface MyAnnotation1 extends java.lang.annotation.Annotation {}

通过反编译可以了解到,注解的本质是默认继承java.lang.annotation.Annotation接口的接口

注解的属性

由于注解本质就是接口,所以在接口可以定义抽象方法,在接口中叫抽象方法,在注解中就叫做属性

在注解中,属性类型(抽象方法的返回值类型)可以写以下几种数据类型:

元注解

用来标记注解的注解

@Retention

标识这个注解怎么保存,是只在代码中,还是编入.class文件中,或者是在运行时可以通过反射访问。

value枚举如下

package java.lang.annotation;

public enum RetentionPolicy {
    //Annotation信息仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了
    SOURCE, 
	//编译器将Annotation存储于类对应的.class文件中。默认行为
    CLASS,
	//编译器将Annotation存储于class文件中,在执行的时也加载到Java的JVM中,因此可以反射性的读取
    RUNTIME    
}

@Target

指定注解用于修饰哪些程序元素

value枚举如下

package java.lang.annotation;

public enum ElementType {
    TYPE,               //类、接口(包括注释类型)或枚举声明

    FIELD,              //字段声明(包括枚举常量)

    METHOD,             //方法声明

    PARAMETER,          //参数声明

    CONSTRUCTOR,        //构造方法声明

    LOCAL_VARIABLE,     //局部变量声明

    ANNOTATION_TYPE,    //注释类型声明

    PACKAGE             //包声明
}
package-info.java

pacakge-info.java是一个 Java 文件,目标是提供一个包级的文档说明及包级的注释。在 Java 5 之前,包级的文档是package.html,是通过 JavaDoc 生成的。而在 Java 5 之后版本,包的描述以及相关的文档都可以写入pacakge-info.java文件。

包级别注释
/**
 * 包级别注释测试
 *
 * @author ygang
 * @since 1.0.0-RELEASE
 * @version 2.0.0-RELEASE
 */
package top.ygang;

使用javadoc命令生成文档

javadoc -encoding UTF-8 -charset UTF-8 top.ygang

image-20230518171207682

包级别注解
@Target(ElementType.PACKAGE)
public @interface MyAnnotation{}


// 在package-info.java中使用
@MyAnnotation
package top.ygang;
包级别变量

package-info.java 中只能声明 default 默认访问权限的类,只能包内访问,其它包、子包都不可访问。

package top.ygang;

class Constant {
    static final String VALUE = "Test";
}

@Documented

生成文档信息的时候保留注解,对类作辅助说明

@Inherited

如果注解类型声明中存在@Inherited元注解,则注解所修饰类的所有子类都将会继承此注解。

@Repeatable

用来标注一个注解在同一个地方可重复使用的一个注解

注解的解析

就是指使用反射技术,获取注解的属性值。

注意:如果想要使用反射来解析注解,前提条件,该注解一定要有元注解@Retention,而且其值一定要为RetentionPolicy.RUNTIME,否则属性不进JVM内存,会有空指针异常

常用方法

获取到类名上的注解的属性值

//获取运行时类的Class对象
Class cls = 带有注解的类的类名.getClass();
//判断是否有注解
if(cls.isAnnotationPresent(注解.class)){
    //获取注解对象
    注解 a = (注解)cls.getAnnotation(注解.class);
    //获取属性值
    a.属性名();
}

获取到方法上的注解的属性值

//获取运行时类的Class对象
Class cls = 带有注解的类的类名.getClass();
Method m = cls.getMethod("方法名");
//判断是否有注解
if(m.isAnnotationPresent(注解.class)){
    //获取注解对象
    注解 a = (注解)m.getAnnotation(注解.class);
    //获取属性值
    a.属性名();
}