| 
 | 
	
 
:runner:概念 
 
概念:说明程序的。给计算机看的 注释:用文字描述程序的。给程序员看的 定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。 概念描述: JDK1.5之后的新特性 说明程序的 使用注解:@注解名称 
:runner:♀️作用 
 
作用分类: 
 
- 编写文档:通过代码里标识的注解生成文档【生成文档doc文档】
 
 - 代码分析:通过代码里标识的注解对代码进行分析【使用反射】
 
 - 编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】
 
  ⛹JDK中预定义的一些注解 
 
@Override:检测被该注解标注的方法是否是继承自父类(接口)的 @Deprecated :该注解标注的内容,表示已过时 @SuppressWarnings :压制警告 一般传递参数all @SuppressWarnings("all") 
注解生成文档案例 
 
要生成doc文档的api类案例: 
/** 
 * 注解javadoc演示 
 * 
 * @author zjq 
 * @version 1.0 
 * @since 1.5 
 */ 
public class AnnoDoc { 
 
    /** 
     * 计算两数的和 
     * @param a 整数 
     * @param b 整数 
     * @return 两数的和 
     */ 
    public int add(int a, int b ){ 
        return a + b; 
    } 
} 
复制代码在对应的类目录进入cmd,执行如下命令 
javadoc AnnoDoc.java 
复制代码执行完成后可以看到生成了很多html和js等前端文件,点击index.html。可以看到如下效果: 
 
  
 
  
 
:bicyclist:自定义注解 
 
格式 
 
元注解 public @interface 注解名称{ 属性列表; } 
:bath:本质 
 
注解本质上就是一个接口,该接口默认继承Annotation接口 
public interface MyAnno extends java.lang.annotation.Annotation {} 
复制代码:couple:属性:接口中的抽象方法 
 
要求: 
 
基本数据类型 String 枚举 注解 以上类型的数组 
 
- 定义了属性,在使用时需要给属性赋值
 
 - 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
 
 - 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
 
 - 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略
 
  案例: 
 
定义: 
public @interface MyAnno { 
    int value(); 
    Person per(); 
    MyAnno2 anno2(); 
    String[] strs(); 
} 
 
public enum Person { 
 
    P1,P2; 
} 
 
使用: 
@MyAnno(value=12,per = Person.P1,anno2 = @MyAnno2,strs="bbb") 
public class Worker { 
 
} 
复制代码:swimmer:元注解:用于描述注解的注解 
 
@Target:描述注解能够作用的位置 ElementType取值: TYPE:可以作用于类上 METHOD:可以作用于方法上 FIELD:可以作用于成员变量上 @Retention :描述注解被保留的阶段 @Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到,自定义注解一般用这个。 @Documented :描述注解是否被抽取到api文档中 @Inherited :描述注解是否被子类继承 
:surfer:♂️在程序使用(解析)注解:获取注解中定义的属性值 
 
之前我们在反射中有通过读取配置文件来创建任意类的对象,执行任意方法。具体反射内容可以看我的这篇文章: Java反射:框架设计的灵魂 之前反射中我们通过读取对应的配置文件然后创建类和执行方法,代码如下: 
/** 
前提:不能改变该类的任何代码。可以创建任意类的对象,可以执行任意方法 
*/ 
 
//1.加载配置文件 
//1.1创建Properties对象 
Properties pro = new Properties(); 
//1.2加载配置文件,转换为一个集合 
//1.2.1获取class目录下的配置文件 
ClassLoader classLoader = ReflectTest.class.getClassLoader(); 
InputStream is = classLoader.getResourceAsStream("pro.properties"); 
pro.load(is); 
 
//2.获取配置文件中定义的数据 
String className = pro.getProperty("className"); 
String methodName = pro.getProperty("methodName"); 
 
 
//3.加载该类进内存 
Class cls = Class.forName(className); 
//4.创建对象 
Object obj = cls.newInstance(); 
//5.获取方法对象 
Method method = cls.getMethod(methodName); 
//6.执行方法 
method.invoke(obj); 
复制代码我们可以通过注解替换上述读取配置文件相关操作。具体代码如下: 注解定义如下: 
/** 
 * 描述需要执行的类名,和方法名 
 * @author zjq 
 */ 
 
@Target({ElementType.TYPE}) 
@Retention(RetentionPolicy.RUNTIME) 
public @interface Pro { 
 
    String className(); 
    String methodName(); 
} 
复制代码通过解析注解配置,执行相关对象创建和执行对象方法。 
 
- 获取注解定义的位置的对象 (Class,Method,Field)
 
 - 获取指定的注解
 
 - 调用注解中的抽象方法获取配置的属性值
 
  代码如下: 
@Pro(className = "com.zjq.javabase.base25.annotation.Demo1",methodName = "show") 
public class ReflectTest { 
    public static void main(String[] args) throws Exception { 
 
        /** 
         * 前提:不能改变该类的任何代码。可以创建任意类的对象,可以执行任意方法 
         */ 
 
        //1.解析注解 
        //1.1获取该类的字节码文件对象 
        Class<ReflectTest> reflectTestClass = ReflectTest.class; 
        //2.获取上边的注解对象 
        //其实就是在内存中生成了一个该注解接口的子类实现对象 
        /* 
 
            public class ProImpl implements Pro{ 
                public String className(){ 
                    return &#34;com.zjq.javabase.base25.annotation.Demo1&#34;; 
                } 
                public String methodName(){ 
                    return &#34;show&#34;; 
                } 
 
            } 
         */ 
        Pro an = reflectTestClass.getAnnotation(Pro.class); 
        //3.调用注解对象中定义的抽象方法,获取返回值 
        String className = an.className(); 
        String methodName = an.methodName(); 
        System.out.println(className); 
        System.out.println(methodName); 
 
 
        //4.加载该类进内存 
        Class cls = Class.forName(className); 
        //5.创建对象 
        Object obj = cls.newInstance(); 
        //6.获取方法对象 
        Method method = cls.getMethod(methodName); 
        //7.执行方法 
        method.invoke(obj); 
    } 
} 
复制代码⛷️案例:通过自定义注解定义一个简单的测试框架 
 
定义一个检测注解 
@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.METHOD) 
public @interface Check { 
} 
复制代码定义一个计算器工具类,并在方法上使用@Check注解 
/** 
 * 小明定义的计算器类 
 * @author zjq 
 */ 
public class Calculator { 
 
    //加法 
    @Check 
    public void add(){ 
        String str = null; 
        str.toString(); 
        System.out.println(&#34;1 + 0 =&#34; + (1 + 0)); 
    } 
    //减法 
    @Check 
    public void sub(){ 
        System.out.println(&#34;1 - 0 =&#34; + (1 - 0)); 
    } 
    //乘法 
    @Check 
    public void mul(){ 
        System.out.println(&#34;1 * 0 =&#34; + (1 * 0)); 
    } 
    //除法 
    @Check 
    public void div(){ 
        System.out.println(&#34;1 / 0 =&#34; + (1 / 0)); 
    } 
 
    public void show(){ 
        System.out.println(&#34;永无bug...&#34;); 
    } 
 
} 
复制代码定义测试框架类并执行测试,把测试异常记录到bug.txt文件中,代码如下: 
/** 
 * 简单的测试框架 
 * 当主方法执行后,会自动自行被检测的所有方法(加了Check注解的方法),判断方法是否有异常, 
 * 记录到文件中 
 * 
 * @author zjq 
 */ 
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;//出现异常的次数 
        BufferedWriter bw = new BufferedWriter(new FileWriter(&#34;bug.txt&#34;)); 
 
 
        for (Method method : methods) { 
            //4.判断方法上是否有Check注解 
            if (method.isAnnotationPresent(Check.class)) { 
                //5.有,执行 
                try { 
                    method.invoke(c); 
                } catch (Exception e) { 
                    //6.捕获异常 
 
                    //记录到文件中 
                    number++; 
 
                    bw.write(method.getName() + &#34; 方法出异常了&#34;); 
                    bw.newLine(); 
                    bw.write(&#34;异常的名称:&#34; + e.getCause().getClass().getSimpleName()); 
                    bw.newLine(); 
                    bw.write(&#34;异常的原因:&#34; + e.getCause().getMessage()); 
                    bw.newLine(); 
                    bw.write(&#34;--------------------------&#34;); 
                    bw.newLine(); 
 
                } 
            } 
        } 
 
        bw.write(&#34;本次测试一共出现 &#34; + number + &#34; 次异常&#34;); 
 
        bw.flush(); 
        bw.close(); 
 
    } 
 
} 
复制代码执行测试后可以在src同级目录查看到bug.txt文件内容如下: 
add 方法出异常了 异常的名称:NullPointerException 异常的原因:null 
div 方法出异常了 异常的名称:ArithmeticException 异常的原因:/ by zero 
本次测试一共出现 2 次异常 
:rowboat:♂️总结 
 
 
- 大多数时候,我们只是使用注解,而不是自定义注解。
 
 - 注解给谁用?
 
 
  
 
- 注解不是程序的一部分,可以理解为注解就是一个标签。
 
 
  |   
 
 
 
 |