注解
- 定义:jdk1.5出现的新特性,与类,接口,枚举属同一层级,可用于包,类,属性,方法,方法参数前用于说明这些元素。
作用分类
- 1、编写文档(注解标识 Javadoc生成文档)
javadoc 类名.java 可根据注解注释得出文档 - 2、代码分析(通过反射,分析注解标注的地方)
- 3、编译检查(代码里标识的注解让编译器能够实现基本的编译检查)
预定义注解
- 1.@Override 保证重写了父类的方法
- 2.@Deprecated 已过时
- 3.@SuppressWarnings(”all“) 压制警告
- 4.@SafeVarargs 压制堆污染(使用有泛型的接收没泛型的)
- 5.@FunctionalInterface 声明该接口是一个函数式接口(里面的抽象方法只有一个)
自定义注解
-
注解的定义分两部分
- 1、元注解
- 2、public @Interface 注解名{}
-
注解的真实面目:本质上是一个接口
先编译,再反编译可知默认继承自java.lang.annotation.Annotation接口
$ javap AnnoDemo.class
Compiled from “AnnoDemo.java”
public interface com.zengqifeng.rocketmqconsumer.annotation.AnnoDemo extends java.lang.annotation.Annotation {
} -
注解的属性(即接口中声明的方法,故定义方法名时最好使用属性名,更易理解)
- 要求:
- 1、属性的返回值类型只能为如下几种
- 基本数据类型
- String类型
- 枚举
- 注解
- 以上类型的数组
- 2、定义了属性,在使用注解时需要给属性赋值
- 1、定义属性时可以使用default关键字设置属性默认值
- 2、如果注解只有一个属性需要赋值,并且属性名是value,赋值时可省略名字,直接写值
- 3、数组类型属性赋值使用{},如果数组里只有一个值,{}可以省略
-
元注解(用于描述注解的注解)
- @Target({ElementType.METHOD, ElementType.TYPE})
- 描述注解作用目标,属性是数组类型,使用{}包裹,如果只有一个,{}可以省略,作用目标可以使类,接口,枚举,方法,属性,方法参数
- @Retention(RetentionPolicy.RUNTIME)
- 注解被保留的阶段(source,class,runtime);当前注解(runtime)会保留到class文件中,并被jvm读取到内存。相比(class )应该是指挥被保留到class文件中,而不会被jvm读取到内存。推测source只会保留在源码阶段。
- @Documented
- Javadoc生成文档时,注解是否被抽取生成在文档中
- @Inherited
- 该注解标注的目标的子孙也继承该注解
- @Target({ElementType.METHOD, ElementType.TYPE})
在程序中解析注解(获取注解中定义的属性值)
- 1、获取注解所在位置的对象
- class,interface,enum,annotation,method,field,parameter等类型上
- 2、获取注解(会生成该注解接口的实现类的对象)
- 3、调用方法获取注解的属性值(实际上是上方生成的对象调用方法获取属性值)
package com.zengqifeng.rocketmqconsumer.annotation;
import java.lang.reflect.Method;
/**
* @author zengqifeng
* @version 1.0
* @date 2019/12/26 14:39
*/
@AnnotationTest(className = "com.zengqifeng.rocketmqconsumer.annotation.TestDemo01", methodName = "show")
public class AnnotationTestDemo {
public static void main(String[] args) throws Exception {
//1、获取注解目标的字节码文件对象
Class<AnnotationTestDemo> annotationTestDemoClass = AnnotationTestDemo.class;
//2、获取注解对象
//此处在内存中生成了该注解接口子类的对象
AnnotationTest annotation = annotationTestDemoClass.getAnnotation(AnnotationTest.class);
//3、获取注解上的属性值
String className = annotation.className();
String methodName = annotation.methodName();
System.out.println(className);
System.out.println(methodName);
//拿到了className和methodName 反射调用任意对象的任意方法
//1、获取calss对象
Class cls = Class.forName(className);
//2、获取构造方法 构造对象(如果单纯只需要无参构造方法,直接cls.newInstance即可)
Object instance = cls.newInstance();
//3、获取方法Method对象
Method show = cls.getMethod("show");
//4、方法调用
show.invoke(instance);
}
}
- 注解和反射实现 自动检测类中标注有Check注解的所有方法,并返回测试报告
package com.zengqifeng.rocketmqconsumer.annotation;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author zengqifeng
* @version 1.0
* @date 2019/12/26 15:44
*/
public class TestDemo {
//加减乘除法的测试
static int a = 1,b = 0;
@Check
public void add() {
System.out.println("1 + 0=" + (a+b) );
}
@Check
public void del() {
System.out.println("1 - 0=" + (a-b) );
}
@Check
public void mul() {
System.out.println("1 * 0=" + (a*b) );
}
@Check
public void div() {
System.out.println("1 / 0=" + (a/b) );
}
public void show() {
System.out.println("没加Check注解,不会被执行,检测不出bug");
}
public static void main(String[] args) throws IOException {
//测试所有Check注解标注的方法 给出测试报告
//1、获取类的Class对象
TestDemo testDemo = new TestDemo();
Class testDemoClass = testDemo.getClass();
//2、获取所有方法(此处获取的是所有的public修饰的方法)
Method[] methods = testDemoClass.getMethods();
int number = 0;
//3、如果方法上有Check注解,那么执行
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("bug.txt"));
for (Method method : methods) {
if (method.isAnnotationPresent(Check.class)) {
try {
method.invoke(testDemo);
} catch (Exception e) {
//4、如果发生异常,捕获异常,记录在文件中
number ++;
bufferedWriter.write(method.getName() + "方法出现异常");
bufferedWriter.newLine();
bufferedWriter.write("异常类型:" + e.getCause().getClass().getSimpleName());
bufferedWriter.newLine();
bufferedWriter.write("异常原因:" + e.getCause());
bufferedWriter.newLine();
bufferedWriter.write("---------------");
bufferedWriter.newLine();
}
}
}
bufferedWriter.write("本次测试出现异常次数:" + number);
bufferedWriter.newLine();
bufferedWriter.flush();
}
}
- bug.txt 文件内容
div方法出现异常
异常类型:ArithmeticException
异常原因:java.lang.ArithmeticException: / by zero
---------------
本次测试出现异常次数:1
- 小结
- 1、以后大多数时候,我们使用注解,而非写注解
- 2、注解被谁使用
- 1、编译器
- 2、解析程序
- 3、注解不属于程序的一部分,只是一种标注(辅助方案)