• 14.Java的注解


    一.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:作用在成员变量上
        • @Retention:标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
          • RetentionPolicy取值:
            • SOURCE:不会被保留在字节码文件中
            • CLASS:保留到class字节码文件中,不会被JVM读取到
            • RUNTIME:保留到class字节码文件中,并被JVM读取到(常用)
        • @Documented:描述注解是否被抽取到api文档中。
        • @Inherited:描述注解是否被子类继承(表示子类是否会自动继承父类注解)
      • 从 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.小结:

    • 大多数情况下是使用注解而不是自定义注解
    • 注解是给编译器和解析程序用的
    • 注解不是程序的一部分,可以理解为注解就是一个标签
  • 相关阅读:
    defineProperty的使用
    js题库全集
    如何将多个文件夹中的文件合并到一个文件夹中
    CYQ.Data V5 MDataTable 专属篇介绍
    读取和写入配置文件内容的方法
    面对代码中过多的if...else的解决方法
    SQL语句--删除掉重复项只保留一条
    获取当前时间
    Stopwatch 类用于计算程序运行时间
    正则表达式手册
  • 原文地址:https://www.cnblogs.com/zhihaospace/p/12227723.html
Copyright © 2020-2023  润新知