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 { }