注解入门梳理

  1. 概念:Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。
  2. 概念描述:
    • 是一种注射机制
    • 是JDK5.0新引入的
    • 相比于注释:
      • 注释,给人看
      • 注解,给程序看
      • 都不属于程序都一部分
      • 都能用于生成JavaDoc
package com.steff;

/**
*Javadoc注解演示
*
* @author steff
* @since 2021.11.27
* @version 1.0
*/

public class Calc {

  /**
   *
   * @param a 整数
   * @param b 整数
   */
  public int add(int a, int b){
      return a + b;
  }
}
  1. 作用分类:

    • 编写文档,通过代码里标识的注解,生成文档【javadoc生成Doc】
    • 编译检查,通过代码里标识的注解,让编译器能够实现基本都编译检查【Override】
    • 代码分析,通过代码里标识的注解,对代码进行分析【使用反射】
  2. JDK中定义的注解:

    • Override:检测被该注解标注的方法是否是继承自父类的
    • Deprecated:被该注解标注的,表示已过时
    • SuppressWarnings: 编辑器中不展示警告

package com.steff;

/**
 * JDK 中预定义的一些注解
 *     * Override:检测被该注解标注的方法是否是继承自父类的
 *     * Deprecated:被该注解标注的,表示已过时
 *     * SuppressWarnings: 编辑器中不展示警告
 */
public class Cat {

    @Override
    public String toString(){
        return super.toString();
    }

    @Deprecated
    public void meow(){
        System.out.println("Meow~");
    }

    public void miaow(){
        System.out.println("Miaow~");
    }

    public void speak(){
        this.miaow();
        this.meow();// IDE里面输入"this."后,候选的meow() 是有删除线的。
    }
    
    // 此方法会在编译器中被警告 "未被使用" / "void方法,无需return",
    // 但是我们可以通过SuppressWarnings注解不让IDE提示
    @SuppressWarnings("all")
    public void mew(){
        return;
    }
}


我们可以看下@Override这个注解是怎么定义的:

package java.lang;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
  1. 自定义注解:
    • 格式:

      • 元注解 // 可选项{@Type, @Target, @Retention}
      • public @interface 注解名称{}
    • 按照格式,我们来自定义一个最简单的注解:

      package com.steff;
      
      public @interface CustomAnnotation {
      }
      
    • 注解原理揭示:
      我们在 命令行 把CustomAnnotation.java编译为CustomAnnotation.class,
      再将CustomAnnotation.class反编译,即可得到这个注解,再java中原本的样子。

      >javac CustomAnnotation.java 
      >javap CustomAnnotation.class
      Compiled from "CustomAnnotation.java"
      public interface com.steff.CustomAnnotation extends java.lang.annotation.Annotation {
      }
      
      

      反编译的结果告诉我们,注解本质其实就是一个默认继承了java.lang.annotation.Annotation的接口。

    • 接下来我们再来看注解中属性

      • 注解属性的类型有以下几种:
        • 基本数据类型
        • String
        • 枚举
        • 注解
        • 以上类型组成的数组
            package com.steff;
      
            @Target(ElementType.TYPE)
            public @interface CustomAnnotation {
                int time = 24*60*60; // 不了解定义普通变量有啥用,期待发掘
                int times() default 7;
                String describe();
                Color color();
                Override an();
                String[] note();
            }
      
      
      • 属性的使用和赋值:
        • 使用注解时,需要给属性赋值,通过default关键字可以设默认值
        • 只有一个属性,且属性名为value,可以直接赋值,不用指定参数名
        • 数组赋值使用{}, 只有一个值时可以省略{}
      package com.steff;
      
      @CustomAnnotation(describe="买可乐", color=Color.YELLOW, note="Let's 买可乐", an=@Override)
      public class Person {
          public void buyCola(){
              System.out.println("买可乐");
          }
      }
      
    • 元注解 // 可选项

      • @Target(ElementType.TYPE) //描述注解能作用的位置
        TPYE -> 只能标注在类上
        METHOD -> 只能标注在方法上
        Field -> 只能标注在成员变量上?
      • @Retention(RetentionPolicy.RUNTIME) // 描述注解被保留的阶段
        • RUNTIME -> 运行时
        • CLASS -> 字节码
        • SOURCE -> 源码
      • @Documented // 描述注解是否被抽取到API文档中
      • @Inherited // 描述注解是否被子类继承
    • 由于不知道这玩意有啥妙用,此处我们不再Demo元注解的用法,有需要可以单独研究

    • 如何读取注解传入到数据?
     package com.steff;
    
     import java.util.Arrays;
    
     @CustomAnnotation(describe="买可乐", color=Color.YELLOW, note="Let's 买可乐", an=@Override)
     public class Person {
    
         public static void main(String[] args) {
    
            // 获取被标注类的字节码文件
           Class personClass = Person.class;
    
           // 获取注解对象
           CustomAnnotation customAnnotation = personClass.getAnnotation(CustomAnnotation.class);
    
           // 调用注解中的抽象方法,获取返回值
           System.out.println(customAnnotation.describe());
           System.out.println(Arrays.stream(customAnnotation.note()).iterator().next());
           System.out.println(customAnnotation.color());
       }
    
    }
    
    
  • 如何理解CustomAnnotation customAnnotation = personClass.getAnnotation(CustomAnnotation.class);
    • 其实就是在内存中实现了该注解接口的子类实现,并重写了注解中的抽象方法,返回了传入的参数:
public class CustomAnnotationImpl implements CustomAnnotation{

        @Override
        public int times() {
            return 0;
        }

        @Override
        public String describe() {
            return "买可乐";
        }

        @Override
        public Color color() {
            return Color.YELLOW;
        }

        @Override
        public Override an() {
            return null;
        }

        @Override
        public String[] note() {
            return "Let's 买可乐".split("");
        }

        @Override
        public Class annotationType() {
            return null; // 这个不会编了
        }
    }
  1. 程序中使用注解
    • 我们写个小练习,通过注解和反射测试Calc类中的方法。
package com.steff;

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

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Check {
}
package com.steff;


public class Calc {

    @Check
    public void add(){
        System.out.println("1 + 0 = " + (1 + 0));
        throw new RuntimeException("你这个有Bug!");
    }

    @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 com.steff;

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

/**
 * 简单的测试框架
 *
 * 当主方法执行后,会自动进行被标记当所有方法(加了Check注解当方法)
 * 监测异常,并记录到文件中
 *
 */
public class TestCheck {

    public static void main(String[] args) throws IOException {
        // 1 创建 待测对象
        Calc cal = new Calc();
        // 2 获取 字节码文件对象
        Class aClass = cal.getClass();
        // 3 获取 所有待测方法

        int count = 0; //出现异常的次数
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("Bug.txt"));
        bufferedWriter.write("--------------------------------------");
        bufferedWriter.newLine();
        for (Method m : aClass.getMethods()) {
            // 4 判断方法上是否有Check注解
            if(m.isAnnotationPresent(Check.class)){
                try {
                    // 5 执行有注解的方法
                    m.invoke(cal);
                } catch (Exception e) {
                    // 6 异常记录
                    count++;
                    bufferedWriter.write(m.getName() + " 出现异常!");
                    bufferedWriter.newLine();
                    bufferedWriter.write("异常名称:" + e.getCause().getClass());
                    bufferedWriter.newLine();
                    bufferedWriter.write("异常原因:" + e.getCause().getMessage());
                    bufferedWriter.newLine();
                    bufferedWriter.write("--------------------------------------");
                    bufferedWriter.newLine();

                }
            }
            
        }
        // 7 测试结果
        bufferedWriter.write("本次一共出现异常" + count + "次。");
        bufferedWriter.flush();
        bufferedWriter.close();
    }


}
  • 执行测试类主方法之后会在当前目录生成测试结果:
--------------------------------------
add 出现异常!
异常名称:class java.lang.RuntimeException
异常原因:你这个有Bug!
--------------------------------------
div 出现异常!
异常名称:class java.lang.ArithmeticException
异常原因:/ by zero
--------------------------------------
本次一共出现异常2次。

内容来源链接

你可能感兴趣的:(注解入门梳理)