注解概念

  注解的本质就是一个继承了 Annotation 接口的接口。在反编译一个注解类(javap命令)时,就可以发现。

  注解可以理解为一种特别的注释,主要是给编译器看的。

mark

JDK中预定义的一些注解

  常用的有下面三个:

  • @Override :检测被注解标注的方法是否为继承自父类(接口)的
  • @Deprecated:表示被注解的内容已经过时
  • @SuppressWarnings:压制警告 一般传递参数all @SuppressWarnings(“all”)

自定义注解

元注解

  • @Target:注解的作用目标。用于指明被修饰的注解最终可以作用的目标是谁,也就是指明,你的注解到底是用来修饰方法的?修饰类的?还是用来修饰字段属性的。
  • @Retention:注解的生命周期
    • RetentionPolicy.SOURCE:当前注解编译期可见,不会写入 class 文件
    • RetentionPolicy.CLASS:类加载阶段丢弃,会写入 class 文件
    • RetentionPolicy.RUNTIME:永久保存,可以反射获取
  • @Documented:注解是否应当被包含在 JavaDoc 文档中
  • @Inherited:是否被子类继承该注解
package cn.xgblack.javastudy.annotation;

import java.lang.annotation.*;

/**
 * ElementType
 *      TYPE作用于类上
 *      METHOD方法上
 *      Field成员变量上
 * RetentionPolicy
 */
@Target(value = {ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MYAnno2 {
}

注解的使用

  • getAnnotation:返回指定的注解
  • isAnnotationPresent:判定当前元素是否被指定注解修饰
  • getAnnotations:返回所有的注解
  • getDeclaredAnnotation:返回本元素的指定注解
  • getDeclaredAnnotations:返回本元素的所有注解,不包含父类继承而来的

使用注解创建指定对象并调用指定方法

自定义一个注解:

package cn.xgblack.javastudy.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Pro {
    String className();
    String methodName();
}

建一个ReflectTest类:

package cn.xgblack.javastudy.annotation;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * @author 小光
 * @date 2019/5/11 12:25
 * className: ReflectTest
 * description: 框架类,在不改变代码的前提下,可以帮助我们创建任意对象,并执行其中的方法
 *              对象的全类名和需要执行的方法名定义在配置文件中
 *              使用反射技术,加载类到内存
 *
 * ***************************************************************************
 * Copyright(C),2018-2019,https://blog.xgblack.cn  .All rights reserved.
 * ***************************************************************************
 */
@Pro(className = "cn.xgblack.javastudy.domain.Student",methodName = "sleep")
public class ReflectTest {

    public static void main(String[] args) throws Exception {
        //解析注解
        //获取该类的字节码文件
        Class<ReflectTest> reflect = ReflectTest.class;
        //获取上边的注解对象
        Pro annotation = reflect.getAnnotation(Pro.class);
        //调用注解对象中定义的方法
        String className = annotation.className();
        String methodName = annotation.methodName();

        Class<?> aClass = Class.forName(className);
        Object obj = aClass.newInstance();
        Method method = aClass.getMethod(methodName);
        method.invoke(obj);
    }
}

这样做的好处就是,不需要改动大量代码,只需要在注解处修改要调用的类的全类名,修改方法名,其他的地方不需要任何修改。

利用注解进行异常检测

自定义一个注解Check

package cn.xgblack.javastudy.demo;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Check {
}

定义一个存在异常情况的Calculator类

package cn.xgblack.javastudy.demo;

import java.sql.SQLOutput;

/**
 * @author 小光
 * @date 2019/5/11 14:38
 * className: Calculator
 * description:
 * ***************************************************************************
 * Copyright(C),2018-2019,https://blog.xgblack.cn  .All rights reserved.
 * ***************************************************************************
 */
public class Calculator {

    @Check
    public void add(){
        String str = null;
        str.toString();
        System.out.println("1 + 0 = " + (1 + 0));
    }
    @Check
    public void sub(){
        System.out.println("1 - 0 = " + (1 - 0));
    }

    @Check
    public void mul(){
        System.out.println("1 + 0 = " + (1 * 0));
    }

    @Check
    public void div(){
        System.out.println("1 + 0 = " + (1 / 0));
    }

    public void show(){
        System.out.println("永无BUG...");
    }

}

写一个测试的类:

package cn.xgblack.javastudy.demo;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;

/**
 * @author 小光
 * @date 2019/5/11 14:37
 * className: TestCheck
 * description: 简单的测试框架
 * 主方法执行后,会自动检测被加了注解的方法,
 * <p>
 * ***************************************************************************
 * Copyright(C),2018-2019,https://blog.xgblack.cn  .All rights reserved.
 * ***************************************************************************
 */
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 num = 0;

        BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt"));

        for (Method method : methods) {
            //4.判断方法上是否有Check方法,
            if (method.isAnnotationPresent(Check.class)) {
                //5.有就执行
                try {
                     method.invoke(c);
                } catch (Exception e) {
                    //6.捕获异常
                    //记录到文件中
                    num++;
                    bw.write(method.getName() + "方法出异常了");
                    bw.newLine();
                    bw.write("异常的名称:" + e.getCause().getClass().getSimpleName());
                    bw.newLine();
                    bw.write("异常的原因:" + e.getCause().getMessage());
                    bw.newLine();
                    bw.write("-------------------------");
                    bw.newLine();
                }
            }
        }
        bw.write("本次测试一共出现" + num + "次异常");
        bw.flush();
        bw.close();



    }

}

Calculator类中有两个地方会发生异常,一个是除0的异常,一个是空指针异常。运行之后,打开生成的bug.txt文件