一.Java注解
- 定义:JDK1.5之后引入的新特性,用来说明程序给计算机看的。
- 作用:编译检查,编写文档,代码分析
1.Java中的内置注解
Java 定义了一套注解,共有 7 个,3 个在 java.lang 中,剩下 4 个在 java.lang.annotation 中。
-
作用在代码的注释:
- @Override:检测被该注解标注的方法是否是继承自父类(父接口)的
- @Deprecated:该注释标注的内容表示已经过时了
- @SuppressWarnings:压制警告
- 一般传递参数all:@SuppressWarnings("all") 表示压制所有警告
- 作用在其他地方注解
- 元注解:
- @Target:标记注解作用的位置(作用在类上还是方法上还是成员变量上)。
- ElementType取值:
- TYPE:作用在类上
- METHOD:作用在方法上
- FIELD:作用在成员变量上
- ElementType取值:
- @Retention:标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
- RetentionPolicy取值:
- SOURCE:不会被保留在字节码文件中
- CLASS:保留到class字节码文件中,不会被JVM读取到
- RUNTIME:保留到class字节码文件中,并被JVM读取到(常用)
- RetentionPolicy取值:
- @Documented:描述注解是否被抽取到api文档中。
- @Inherited:描述注解是否被子类继承(表示子类是否会自动继承父类注解)
- @Target:标记注解作用的位置(作用在类上还是方法上还是成员变量上)。
- 从 Java 7 开始,额外添加了 3 个注解:
- @SafeVarargs:Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
- @FunctionalInterface:Java 8 开始支持,标识一个匿名函数或函数式接口。
- @Repeatable:Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
- 元注解:
2.自定义注解
- 基本格式:
1 元注解 2 public @interface 注解名称{ 3 属性列表 4 }
- 本质:注解本质就是一个接口,该接口默认继承Annotation接口
- public interface MyAnno extends java.lang.annotation.Annotation{}
- 属性:接口中抽象方法
- 要求:
- 属性的返回类型
- 8大基本类型
- String
- 枚举
- 注解
- 以上类型的数组
- 使用注解时需要给属性进行赋值
- 如果定义属性时使用default给属性赋默认值后,使用注解时可以不对属性进行赋值
- 如果只有一个属性需要赋值,并且属性名为value时,value可以省略,直接给值即可。
- 数组赋值时,值使用{}包裹,如果数组至于一个值,则{}可以省略
- 属性的返回类型
- 要求:
Anno2:
1 public @interface Anno2 { 2 String name(); 3 }
MyEnum:
1 public enum MyEnum { 2 type1,type2 3 }
MyAnno:
1 public @interface MyAnno { 2 int age(); 3 String name() default "张三"; 4 MyEnum type(); 5 Anno2 anno2(); 6 String[] strs(); 7 }
TestAnno:
1 @MyAnno(age = 24, type = MyEnum.type1, anno2 = @Anno2(name = "anno2"), strs = {"s1", "s2"}) 2 public class TestAnno { 3 4 }
3,在程序使用(解析)注解:获取注解定义的属性值
- 步骤:
- 获取注解定义的位置的对象(Class,Method,Field)
- 获取指定的注解
- getAnnotation(Class):实质上是在内存中生成了一个该注解的接口的子类实现对象
- 调用注解中的抽象方法获取到配置的属性
注解类:Pro
1 @Target(ElementType.TYPE) 2 @Retention(RetentionPolicy.RUNTIME) 3 public @interface Pro { 4 5 String className(); 6 String methodName(); 7 String parameterType(); 8 }
需要生成的类:TestClass
1 public class TestClass { 2 private void show(String s){ 3 System.out.println(s); 4 } 5 }
主类:ReflectTest
1 @Pro(className = "day11.TestClass",methodName = "show",parameterType = "java.lang.String") 2 public class ReflectTest { 3 4 public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { 5 6 //1.解析注解 7 //1.1获取该类的字节码文件对象 8 Class<ReflectTest> reflectTestClass = ReflectTest.class; 9 //2.获取注解对象 10 Pro an = reflectTestClass.getAnnotation(Pro.class); 11 //实质上是在内存中生成了一个该注解的接口的子类实现对象 12 /* 13 public class ProImpl implements Pro{ 14 public String className(){ 15 return "day11.TestClass"; 16 } 17 18 public String methodName(){ 19 return "show"; 20 } 21 22 public String parameterType(){ 23 return "java.lang.String"; 24 } 25 } 26 */ 27 28 //3.调用注解对象中定义的抽象方法,获取返回值 29 String className = an.className(); 30 String methodName = an.methodName(); 31 Class parameterType = Class.forName(an.parameterType()); 32 33 //4.加载该类进内存 34 Class cls = Class.forName(className); 35 36 //5.创建对象 37 Object obj = cls.newInstance(); 38 39 //6.获取方法对象 40 Method method = cls.getDeclaredMethod(methodName,parameterType); 41 method.setAccessible(true); 42 43 //7.执行方法 44 method.invoke(obj,"day11.TestClass--show"); 45 } 46 }
结果:
4.案例:简单的测试框架
注解类:Check
1 @Target(ElementType.METHOD) 2 @Retention(RetentionPolicy.RUNTIME) 3 public @interface Check { 4 }
计算器类:Calculator
1 public class Calculator { 2 3 //加法 4 @Check 5 public void add() { 6 System.out.println("1 + 0 =" + (1 + 0)); 7 } 8 9 //减法 10 @Check 11 public void sub() { 12 System.out.println("1 - 0 =" + (1 - 0)); 13 } 14 15 //乘法 16 @Check 17 public void mul() { 18 System.out.println("1 * 0 =" + (1 * 0)); 19 } 20 21 //除法 22 @Check 23 public void div() { 24 System.out.println("1 / 0 =" + (1 / 0)); 25 } 26 27 //不检测的方法 28 public void show() { 29 System.out.println("不检测的方法"); 30 } 31 }
测试Check类:TestCheck
public class TestCheck { public static void main(String[] args) throws IOException { //1.创建计算器对象 Calculator c = new Calculator(); //2.获取字节码文件对象 Class cls = c.getClass(); //3.获取所有方法 Method[] methods = cls.getMethods(); //记录出现异常次数 int number = 0; //将异常信息记录在bug.txt中 BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt")); for (Method method : methods) { //4.判断方法上是否有Check注解 if (method.isAnnotationPresent(Check.class)) { try { //5.有Check注解,执行 method.invoke(c); } catch (Exception e) { //6.捕获异常 //记录异常信息 number++; bw.write(method.getName() + "方法出现异常"); bw.newLine(); bw.write("异常的名称:" + e.getCause().getClass().getSimpleName()); bw.newLine(); bw.write("异常的原因:" + e.getCause().getMessage()); bw.newLine(); bw.write("------------------"); } } } bw.write("本次测试一共出现" + number + "次异常"); bw.flush(); bw.close(); } }
结果:
5.小结:
- 大多数情况下是使用注解而不是自定义注解
- 注解是给编译器和解析程序用的
- 注解不是程序的一部分,可以理解为注解就是一个标签