4、注解

1、自定义注解

特殊属性:value 属性

  • 如果只有一个 value 属性的情况下,使用 value 属性的时候可以省略 value 名称不写
  • 但是如果有多个属性,且多个属性没有默认值,那么 value 名称是不能省略的

属性的数据类型

  • 八种基本数据类型:byte、short、int、long、float、double、boolean、char
  • String、Class、注解类型、枚举类
  • 以上类型的一维数组形式
public @interface 注解名称 {
    public 属性类型 属性名() default 默认值; // 默认值可以不写
}

// 注解使用
public @interface MyAnnotation {
    public String name();
}
@MyAnnotation(name = "张三")

// 注解使用, key 的名称是 value, key 可以省略不写
public @interface MyAnnotation {
    public String value();
}
@MyAnnotation("张三")

2、元注解

元注解:就是注解的注解

  • @Target:约束自定义注解只能在哪些地方使用,也就是说给谁添加注解
  • @Retention:声明注解的生命周期
  • @Documented:是否在文档注释中体现
  • @Inherited:是否可以被继承到所标记的类的子类
  • @Repeatable:是否可以重复使用

2.1、@Target

@Target 中可使用的值定义在 ElementType 枚举类中,常用值如下

  • TYPE:类、接口(包括注解)、枚举
  • FIELD:成员变量(属性)
  • METHOD:成员方法
  • PARAMETER:方法参数
  • CONSTRUCTOR:构造器
  • LOCAL_VARIABLE:局部变量
  • PACKAGE:包
  • ANNOTATION_TYPE:注解
  • TYPE_PARAMETER:能在类型变量的声明语句中(泛型)
  • TYPE_USE:能在使用类型的任何语句中

2.2、@Retention

@Retention 中可使用的值定义在 RetentionPolicy 枚举类中,常用值如下

  • RetentionPolicy.SOURCE:注解只作用在源码阶段,生成的字节码文件中不存在
  • RetentionPolicy.CLASS:注解作用在源码阶段,字节码文件阶段,运行阶段不存在,不会被加载到 JVM 中,默认值
  • RetentionPolicy.RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段(开发常用)
@Retention 源码阶段 字节码文件阶段 运行阶段
SOURCE × ×
CLASS ×
RUNTIME

2.3、@Documented

@Documented
使用 Javadoc 工具可以从程序源代码中抽取类、方法、成员等注释形成一个和源代码配套的 API 帮助文档,而该工具抽取时默认不包括注解内容

  • 被它修饰的注解将被 Javadoc 工具提取成文档
  • 定义为 @Documented 的注解必须设置 @Retention(RetentionPolicy.RUNTIME)

2.4、@Inherited

@Inherited 是否可以被继承到所标记的类的子类
如果一个超类被该注解标记过的注解进行注解时,如果子类没有被任何注解应用时,该子类就继承超类的注解

2.5、@Repeatable

@Repeatable 是否可以重复使用,在一个地方使用多次相同的注解

// 注解类
@Repeatable(value = ManTypes.class)
public @interface ManType {
    String value() default "";
}
// 注解容器类
public @interface ManTypes {
    ManType[] value();
}
// @ManTypes({@ManType("歌手"), @ManType("超人")})
@ManType(value = "歌手")
@ManType(value = "超人")
public class Test {
}

3、注解解析

注解的操作中经常需要进行解析,注解的解析就是判断是否存在注解,存在注解就解析出内容
解析注解的技巧:注解在哪个成分上,我们就先拿哪个成分对象

  • 作用在类上,则要该类的 Class 对象,再来拿上面的注解
  • 作用方法,则要获得该方法对应的 Method 对象,再来拿上面的注解
  • 作用在变量上,则要获得该成员变量对应的 Field 对象,再来拿上面的注解

与注解解析相关的接口

  • Annotation:注解的顶级接口,注解都是 Annotation 类型的对象
  • AnnotatedElement:该接口定义了与注解解析相关的解析方法

所有的类成分 Class、Method、Field、Constructor,都实现了 AnnotatedElement 接口,它们都拥有解析注解的能力

方法 说明
boolean isAnnotationPresent(Class<Annotation> annotationClass) 判断当前对象是否使用了指定的注解,如果使用了则返回 true,否则 false
T getDeclaredAnnotation(Class<T> annotationClass) 根据注解类型获得对应注解对象
Annotation[] getDeclaredAnnotations() 获得当前对象上使用的所有注解,返回注解数组
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Book {
    String value();
    double price() default 100;
    String[] author();
}
@Book(value = "《情深深雨濛濛》", price = 99.9, author = {"琼瑶", "dlei"})
public class BookStore {
    @Book(value = "《三少爷的剑》", price = 399.9, author = {"古龙", "熊耀华"})
    public void test() {}
}
public class Test {
    @Test
    public void parseClass() {
        Class c = BookStore.class; // 先得到类对象
        // 判断这个类上面是否存在这个注解
        if(c.isAnnotationPresent(Book.class)) {
            Book book = (Book) c.getDeclaredAnnotation(Book.class); // 直接获取该注解对象
            System.out.println(book.value());
            System.out.println(book.price());
            System.out.println(Arrays.toString(book.author()));
        }
    }

    @Test
    public void parseMethod() throws NoSuchMethodException {
        Class c = BookStore.class;              // 先得到类对象
        Method m = c.getDeclaredMethod("test"); // 再得到方法对象
        // 判断这个类上面是否存在这个注解
        if(m.isAnnotationPresent(Book.class)) {
            Book book = (Book) m.getDeclaredAnnotation(Book.class); // 直接获取该注解对象
            System.out.println(book.value());
            System.out.println(book.price());
            System.out.println(Arrays.toString(book.author()));
        }
    }
}

4、有注解就执行

public class Test {
    public void test1(){
        System.out.println("test1");
    }

    @MyTest
    public void test2(){
        System.out.println("test2");
    }

    @MyTest
    public void test3(){
        System.out.println("test3");
    }

    // 启动菜单: 有注解的才被调用
    public static void main(String[] args) throws Exception {
        Test t = new Test();
        Class c = Test.class; // 获取类对象
        Method[] methods = c.getDeclaredMethods(); // 提取全部方法
        // 遍历方法, 看是否有 MyTest 注解, 有就跑它
        for (Method method : methods) {
            if(method.isAnnotationPresent(MyTest.class)){
                method.invoke(t); // 跑它
            }
        }
    }
}

5、预制注解

预制注解就是 Java 语言自身提供的注解

注解 说明
@auther 标明开发该类模块的作者,多个作者之间使用 , 分割
@version 标明该类模块的版本
@see 参考转向,也就是相关主题
@since 从哪个版本开始增加的
@param 对方法中某参数的说明,如果没有参数就不能写
@return 对方法返回值的说明,如果方法的返回值类型是 void 就不能写
@execption 对方法可能抛出的异常进行说明
@Override 限定重写父类方法,该注解只能用于方法
@Deprecated 用于表示所修饰的元素(类、方法等)已过时
@SuppressWarnings 抑制编译器警告
posted @ 2023-06-11 00:07  lidongdongdong~  阅读(9)  评论(0编辑  收藏  举报