java注解

一、java注解用于为java代码提供元数据。提供数据来解释程序代码。注解并非所解释的代码的一部分,不直接影响代码执行

所有注解会自动继承java.lang.annotation这个接口,所以不能再去继承别的接口或类。

 

解析一个类/方法的注解有2种方式:

1、编译期的直接扫描

  编译器在对java代码编译为字节码的过程中会检测到某个类/方法被一些注解修饰,这时它就会对这些注解进行某些处理。例:@Override。

2、运行期反射

  下文中会举例。

 

对应的使用场景:

1、提供信息给编译器,编译器利用注解来探测错误/警告信息。

2、运行时的处理,某些注解可以在代码运行时通过反射获取特定代码。

 

回到上文中提到的举例,通过这个🌰更具体的了解注解在实际代码中的作用。

🌰:

 声明一个用于检查bug的注解:CheckBug.java:

@Retention(RetentionPolicy.RUNTIME)
public @interface CheckBug {
}

 

给各个需要检查bug的方法,配置@CheckBug注解:

public class MyMethods {
    @CheckBug
    public void add(int a, int b) {
        System.out.printf("加法:%d + %d = %d\n", a, b, a + b);
    }

    @CheckBug
    public void sub(int a, int b) {
        System.out.printf("减法:%d - %d = %d\n", a, b, a - b);
    }

    @CheckBug
    public void div(int a, int b) {
        System.out.printf("减法:%d / %d = %d\n", a, b, a / b);
    }
}

 

方法调用入口:CheckMyMethodsBugMain.java:

public class CheckMyMethodsBugMain {
    public static void main(String[] args) {
        StringBuilder log = new StringBuilder();
        MyMethods noBug = new MyMethods();
        Class clz = noBug.getClass();
        Method[] ms = clz.getDeclaredMethods();  // 通过反射获取该类的方法

        int errorNum = 0;
        for (Method m : ms) {
            if (m.isAnnotationPresent(CheckBug.class)) {  // 过滤被@CheckBug标注过的方法
                try {
                    m.setAccessible(true);
                    m.invoke(noBug, 6, 0);  // 反射方式调用方法,传入参数(a=6,b=0)
                } catch (Exception e) {
                    errorNum++;
                    log.append(m.getName() + "方法有个错误:" + e.getCause() + "\n");  // 报错信息日志输出
                }
            }
        }
        log.append(clz.getSimpleName() + "类有 " + errorNum + "个错误");
        System.out.println(log);
    }
}

 

运行结果:

加法:6 + 0 = 6
减法:6 - 0 = 6
div方法有个错误:java.lang.ArithmeticException: / by zero
MyMethods类有 1个错误

 

上例中通过反射方式提取被@CheckBug注解标注过的方法,在主函数里进行相关错误捕捉和信息输出的处理

 

二、元注解:修饰注解的注解。有5种类型:@Retention、@Target、@Documented、@Inherited、@Repeatable。

@Retention:解释说明注解的存活时间。

SOURCE: 当前注解编译器可见,只保留在源码阶段,不会写入class文件。

CLASS:会写入class文件,类加载阶段丢弃。保留到编译进行时,不会加载到JVM中。

RUNTIME:永久保存。会被加载到JVM中,可通过反射获取。

 

@Target:指定注解应用的地方。

 

@Documented:能够将注解中的元素包含到javaDoc中。

 

@Inherited:如果一个超类 被 @Inherited注解过的注解 进行注解的话,那么如果它的子类如果没有被任何注解应用的话,这个子类就继承了超类的注解。

举例:

被@Inherited注解过的注解:Test.java:

@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
}

被@Test注解过的超类:A.java:

@Test
public class A {
}

继承A.java的子类:B.java:

public class B extends A{
}

那么:子类B.java也拥有了@Test注解。

 

@Repeatable:注解的值可以同时取多个。(用法有点绕,不常用。可暂时不细看)

Persons.java:

public @interface Persons {
    Person[] value();
}

Person.java:

(@Repeatable注解了Person,后面括号中的类相当于一个注解容器Persons.java。注解容器就是用来存放其他注解的地方,它本身也是一个注解。里面必须有一个value属性。属性类型是一个被@Repeatable注解过的注解数组。)

@Repeatable(Persons.class)
public @interface Person {
    String role() default "";
}

SuperMan.java:

@Person(role="artist")
@Person(role="coder")
@Person(role="pm")
public class SuperMan {
}

 

posted @ 2023-11-22 09:48  DoubleFishes  阅读(36)  评论(0编辑  收藏  举报