? 注解,可以理解为标签,是一种特殊的“注释”,用来标识或解释Java代码,是给机器看的(而注释是给程序员看的)。
? 注解的定义:注解也叫元数据,跟类、接口、枚举是同一个层次的,也是java的一种类型,在Java SE 5.0开始引入,放在Java源码的类、方法、字段、参数前面,用来进行注释或说明。
注解的作用有以下三类:
标准注解
标准注解是指JDK自带的几个注解,主要有这几个:
@SuppressWarnings元注解
元注解是用来修饰其他注解的,主要有以下几种:
@Retention:解释说明了注解的生命周期,有以下三种取值
RetentionPolicy.SOURCE:注解只在源码阶段保留,在编译器进行编译时将注解丢弃或忽视RetentionPolicy.CLASS:注解只保留到编译进行时,不会被加载进JVMRetentionPolicy.RUNTIME:注解保留到程序运行时,会被加载进入JVM中,所以在程序运行时可以获取到它们@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    int value();
}如果定义注解时
@Retention不存在,则该Retention默认为RetentionPolicy.CLASS,但我们通常自定义的注解都是RUNTIME时使用,所以必须要加上@Retention(RetentionPolicy.RUNTIME
@Documented:用来将注解中的元素包含到Javadoc文档中
@Target:定义注解所修饰的对象范围,有以下7种取值
ElementType.PACKAE:可以给一个包进行注解ElementType.TYPE:可以给一个类型进行注解,比如类,接口,枚举等ElementType.CONSTRUCTOR:可以给构造方法进行注解ElementType.METHOD:可以给方法进行注解ElementType.PARAMETER:可以给一个方法内的参数进行注解ElementType.FIELD:可以给属性进行注解ElementType.LOCAL_VARIABLE:可以给局部变量进行注解@Target(ElementType.METHOD) // 限制了注解MyAnnotation只能用于解释说明某个方法
public @interface MyAnnotation {
    int value();
}@Inherited:定义子类是否可以继承父类定义的注解,如果一个class使用了@Inherited修饰的注解,那么这个注解将被用于这个class的子类。
- 这个元注解只对类的继承起效,对接口的继承无效。
- 适用前提是:子类没有被任何注解应用。
// 元注解@Inherited 作用于 注解MyAnnotation
@Inherited
public @interface MyAnnotation {
    int value();
}
// 注解MyAnnotation作用于 class A
@MyAnnotation
public class A {
}
// class B 继承了 class A,而且class B没有被其他注解应用
// 则class B继承了class A的注解@MyAnnotation
public class B extends A {
}自定义注解
注解的定义格式:通过@interface关键字自定义
public @interface MyAnnotation {
}
// 这样就定义了一个注解了注解本质上就是一个接口,该接口默认继承Annotation接口
public interface MyAnnotation extends java.lang.annotation.Annotation {}
注解的属性
注解的属性其实就是注解接口中的抽象方法,只是叫做属性,不叫方法
public @interface MyAnnotation {
    int id();
    String name();
}// 这个注解有两个属性:id 和 name注解的属性是用无参的方法的形式来声明的,方法名就是属性名,比如上面的id和name,方法返回值就是属性的类型(下面介绍有多少种类型)
注解属性的类型
属性的类型(方法的返回值) 有以下几种取值:
注解属性的赋值
属性赋值的格式是:注解(属性名1=value1,属性名2=value2...),用逗号分隔。
在定义了属性之后,在使用注解的属性时需要赋值,有以下三种情况:
public @interface MyAnnotation {
    int id() default 0;
    String name();
}// 这个注解有两个属性:id 和 name
// 使用注解
@MyAnnotation(id=1,name="zhangsan")
public class A {
}获取注解
获取某个对象上的所有注解,需要使用反射技术。
注意:反射的时间成本比较高,所以注解的使用需要慎重。
主要步骤有3个:
<T extends Annotation> getAnnotation(Class<T> annotationClass):返回指定类型的注解,如果不存在返回nullAnnotation[] getDeclaredAnnotations(): 返回该元素上的所有注解还有一个API,这个方法在是Class类对象的成员方法,用于某个类是否应用了某个注解:
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
注解的获取
import java.lang.annotation.*;
/**
 * 定义一个注解
 * 注解在程序运行时使用需要加上@Retention(RetentionPolicy.RUNTIME)
 */
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    // 定义两个属性
    int id() default -1;
    String type();
}import java.lang.reflect.*;
/**
 * 定义一个被注解MyAnnotation应用的类
 */
@MyAnnotation(id=1,type="Class")
public class ClassA {
    // 注解作用于成员
    @MyAnnotation(id=2,type="Field")
    public int field;
    // 注解作用于方法
    @MyAnnotation(id=3,type="Method")
    public void method() {
    }
    public static void main(String[] args) throws NoSuchFieldException, SecurityException, NoSuchMethodException {
        // 1. 获取类上的注解
        // 先判断类是不是应用了该注解
        boolean flag = ClassA.class.isAnnotationPresent(MyAnnotation.class);
        // 如果应用了,获取他的属性
        if(flag) {
            MyAnnotation annotation = ClassA.class.getAnnotation(MyAnnotation.class);
            int id = annotation.id();
            String type = annotation.type();
            System.out.println("ClassA id: " + id);
            System.out.println("ClassA type: " + type);
            // 输出:
            // ClassA id: 1
            // ClassA type: Class
        }
        // 2. 获取成员变量上的注解
        // 先利用反射获取成员变量field
        Field f = ClassA.class.getField("field");
        flag = f.isAnnotationPresent(MyAnnotation.class);
        if (flag) {
            MyAnnotation annotation1 = f.getAnnotation(MyAnnotation.class);
            System.out.println("Field id: " + annotation1.id());
            System.out.println("Field type: " + annotation1.type());
            // 输出:
            // Field id: 2
            // Field type: Field
        }
        // 3.获取方法上的注解
        Method m = ClassA.class.getMethod("method");
        flag = f.isAnnotationPresent(MyAnnotation.class);
        if (flag) {
            MyAnnotation annotation2 = m.getAnnotation(MyAnnotation.class);
            System.out.println("Method id: " + annotation2.id());
            System.out.println("Method type: " + annotation2.type());
            // 输出:
            // Method id: 3
            // Method type: Method
        }
    }
}简单的测试小框架:测试类中的方法是否有问题
执行加了注解的方法,如果有bug,就输出到log文件中。
import java.lang.annotation.*;
/**
 * 定义一个注解,用于方法上
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Check {
}
public class TestClass {
    @Check
    public void add(int a, int b) {
        System.out.println("a + b = " + (a + b));
    }
    @Check
    public void sub(int a, int b) {
        System.out.println("a - b = " + (a - b));
    }
    @Check
    public void div(int a, int b) {
        System.out.println("a / b = " + (a / b));
    }
}import java.io.*;
import java.lang.reflect.*;
public class CheckClass {
    public static void main(String[] args) throws IOException {
        // 创建TestClass对象
        TestClass tc = new TestClass();
        // 获取Class对象
        Class<?> cls = tc.getClass();
        // 获取所有的方法
        Method[] methods = cls.getMethods();
        // 出错的次数
        int errorNum = 0;
        // log文件
        BufferedWriter bw = new BufferedWriter(new FileWriter("log.txt"));
        // 遍历所有的方法,有注解Check的方法执行
        for (Method m : methods) {
            // 判断是否有Check注解
            if (m.isAnnotationPresent(Check.class)) {
                // 执行方法
                try {
                    m.invoke(tc, 1, 0);
                } catch (Exception e) {
                    // 将异常信息写入log文件
                    errorNum++;
                    bw.write(m.getName() + "出现异常了");
                    bw.newLine();
                    bw.write("异常类型为:" + e.getCause().getClass().getName());
                    bw.newLine();
                    bw.write("异常原因为:" + e.getCause().getMessage());
                    bw.newLine();
                    bw.write("------------------------------------");
                    bw.newLine();
                }
            }
        }
        bw.write("本次测试一共出现了" + errorNum + "次异常");
        bw.newLine();
        bw.flush();
        bw.close();
    }
}最后运行结果为:
$ java CheckClass
a + b = 1
a - b = 1打开log.txt文件如下:
div出现异常了
异常类型为:java.lang.ArithmeticException
异常原因为:/ by zero
------------------------------------
本次测试一共出现了1次异常原文:https://www.cnblogs.com/LucasBlog/p/12198887.html