Java注解

AI摘要:

对应《疯狂Java讲义(第5版)》14 章节

Annotation 注解概述

写在接口、类、属性、方法上的一个标签,或者说是一个特殊形式的注释

可以在编译、类加载、运行时被读取,注解在代码运行时是可以被反射读取并进行相应的操作,而如果没有使用反射或者其他检查,那么注解是没有任何真实作用的,也不会影响到程序的正常运行结果

提供为程序元素(接口、类、属性、方法)设置元数据(描述数据的数据)的方法,存储在 “name = value” 对中

通常来说注解分为以下三类

  • 基本注解 – Java提供的基础注解,标明过期的元素/标明是复写父类方法的方法/标明抑制警告。
  • 元注解 – java内置的注解,标明该注解的使用范围、生命周期等。
  • 自定义注解 – 第三方定义的注解,含义和功能由第三方来定义和实现。

基本注解

位于 java.lang.*

  • @Override 标记一个方法是覆写父类方法

    可以避免方法签名错误

  • @Deprecated 标记一个元素为已过期,避免使用

    - since: 指定从哪个版本被标记为过时

    - forRemoval: 将来是否会被删除

支持的元素类型为:CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE

  • @SuppressWarnings 不输出对应的编译警告

    可以抑制该元素及其所有子元素的编译器警告

    1
    @SuppressWarnings(value = "unchecked")
  • @SafeVarargs 抑制堆污染

    Heap pollution(堆污染), 指的是当把一个不带泛型的对象赋值给一个带泛型的变量时, 就有可能发生堆污染。堆污染在编译时并不会报错, 只会在编译时提示有可能导致堆污染的警告. 在运行时,如果发生了堆污染, 那么就会抛出类型转换异常。

    1
    2
    3
    4
    5
    6
    7
    // list 定义时未指定泛型,实际上集合中存储为Integer类型
    List list = new ArrayList<Integer>();
    list.add(1);
    // 将无泛型的list对象,赋值给指定类型的strList变量, 此处已经发生堆污染
    List<String> strList = list;
    // 抛出异常 java.lang.ClassCastException
    String str = strList.get(0);
  • @FunctionalInterface 表明这是一个函数式接口

    函数式接口:只含一个抽象方法的接口

元注解

位于 java.lang.annotation.*, 标注在自定义注解处

  1. @Retention 标明自定义注解的生命周期

    从编写Java代码到运行主要周期为源文件Class文件运行时数据,@Retention则标注了自定义注解的信息要保留到哪个阶段,分别对应的value取值为SOURCECLASSRUNTIME默认值为class

    1
    2
    3
    4
    5
    6
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Retention {
    RetentionPolicy value();
    }
  2. @Target 描述自定义注解的使用范围

    说明
    TYPE类、接口、注解、枚举
    FIELD成员变量
    METHOD方法
    PARAMETER方法参数
    CONSTRUCTOR构造函数
    LOCAL_VARIABLE局部变量(如循环变量、catch参数)
    ANNOTATION_TYPE注解
    PACKAGE
    TYPE_PARAMETER泛型参数 jdk1.8 Java8
    TYPE_USE任何元素 jdk1.8 Java8
  3. @Inherited 是否可以被标注类的子类继承

    1
    2
    3
    4
    5
    6
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    public @interface Inheritable
    {
    }

    在自定义的注解标注到某个类时,该类的子类会继承这个自定义注解。这里需要注意的是只有当子类继承父类的时候,注解才会被继承,类实现接口,或者接口继承接口,都是无法获得父接口上的注解声明的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // 使用@Inheritable修饰的Base类
    @Inheritable
    class Base
    {
    }
    // TestInheritable类只是继承了Base类,
    // 并未直接使用@Inheritable Annotiation修饰
    public class InheritableTest extends Base
    {
    public static void main(String[] args)
    {
    // 打印TestInheritable类是否具有@Inheritable修饰
    System.out.println(InheritableTest.class
    .isAnnotationPresent(Inheritable.class));
    }
    }
  4. @Repeatable 是否可以重复标注

  5. @Documented 是否在生成的JavaDoc文档中体现

    被标注该注解后,生成的javadoc中,会包含该注解

自定义注解 (@interface)

1
2
3
4
5
6
public @interface MyTag{
//成员变量以方法形式定义
//default 指定初始值
String name() default "yeeku";
int age() defalue 32;
}

1
2
@MyTag(name = "xx", age = 6)
public Class Test{}
  • 提取注解信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @MethodAnnotation(name="cngo", age=6)
    public void test() throws NoSuchMethodException, SecurityException, ClassNotFoundException{
    Annotation[] aArray = Class.forName("Test").getMethod("test").getAnnotations();
    for(var an: aArray){
    if(an instanceof MethodAnnotation){
    System.out.println(((MethodAnnotation) an).name());
    System.out.println(((MethodAnnotation) an).age());
    }
    System.out.println(an);
    }
    }
  • 重复注解

    注解容器 与 @Repeatable

  • 类型注解

  • 编译时处理注解

    APT(Annotation Processing Tool),注解处理工具,找出原文件注解信息并进行处理,生成额外的源文件或其他文件

    1
    2
    //指定注解处理器
    javac -processor HibernateAnnotationProcessor Person.java

注解处理器需要实现 javax.annotation.processing 包下的 Process 接口,可继承 AbstractProcessor 方式实现注解处理器


Java注解
https://blog.cngo.rr.nu/posts/6ff9.html
作者
cngo
发布于
2024年9月1日
许可协议