跳过正文
  1. 文章/
  2. Java/
  3. JavaSE/
  4. JavaSE高级/

8、注解

·2676 字·6 分钟· loading · loading · ·
Java JavaSE JavaSE高级
GradyYoung
作者
GradyYoung
JavaSE高级 - 点击查看当前系列文章
§ 8、注解 「 当前文章 」

什么是注解
#

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

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

与注释的区别
#

  • 注释:用来解释说明,是给程序员看的

  • 注解:用来解释说明,是给程序看的

功能
#

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

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

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

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

使用注解
#

  • 将注解写在类,方法,成员变量的上面即可

  • 如果注解有成员(无默认值),那么在使用的时候必须在注解名后指明成员的值

  • 如果一个注解中有多个属性的时候,我们在使用注解的时候,要给所有的属性附上值,之间用逗号分隔。

    • @MyAnnotation(name="lucy",age=12)
  • 在注解中,有一个非常特别的属性名,叫做value,如果一个注解中,只有一个属性,而且这个属性的名字叫做value的话,那我们在使用注解的时候,就可以不写该属性的名字,例如@Annotation("val")

@AnnotationName
public void show() {}

@AnnotationName
int i;

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

注解的分类
#

预定义注解
#

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

  • @Override:检测该方法是否是重写的方法,如果发现父类、实现的接口中未定义该方法,会在编译器报错
  • @Deprecated:标记过期的方法和类,如果调用了此方法和类,会有编译警告
  • @SuppressWarnings:指示编译器在编译时忽略声明的警告,以下为value常用可选值
    • deprecation:使用过期方法、类警告
    • unchecked:执行了未进行检查的转换时的警告,如集合未使用泛型
    • fallthrough:Swith代码块未使用break的警告
    • path:类路径、源文件路径不存在的警告
    • serial:在可序列化类上缺少serialVersionUID定义的警告
    • finally:任何finally子句不能正常完成时的警告
    • all:所有警告
  • @FunctionalInterface:Java8支持,检测接口是否是一个函数式接口,如果不是(接口中没有抽象方法,抽象方法的个数多于一个)则编译失败

自定义注解
#

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

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

注解的实质
#

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

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

注解的属性
#

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

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

  • 基本数据类型
  • String
  • 枚举
  • 注解
  • 还有以上几种数据类型的数组类型

元注解
#

用来标记注解的注解

@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内存,会有空指针异常

常用方法
#

  • getAnnotation(Class<A> annatationClass):取得该元素指定类型的注解
  • Annotation[] getAnnotations():返回此元素上存在的所有注解的数组,包括从父类继承的
  • Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注解的数组,不包括父类的注解
  • boolean isAnnotation():判断元素是否是一个注解
  • boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) :判断元素是否存在指定类型的注解

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

//获取运行时类的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.属性名();
}
JavaSE高级 - 点击查看当前系列文章
§ 8、注解 「 当前文章 」