天天看点

Annotation(注解)注解

注解

1.概念:用来说明程序的,给计算机看的,注解(Annotation),也叫元数据。一种代码级别的说明,它是jdk1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。

作用分类:

​ 编写文档:通过代码里标识的元数据(注解)生成文档(生成文档doc文档)

package annotation;


public class AnnoDemo1 {
    /**
     *
     * @param a 变量a
     * @param b 变量b
     * @return  a和b的和
     */
    public static int add(int a,int b){
        return a+b;
    }

    public static void main(String[] args) {
        System.out.println(add(3,5));
    }
}
           

Annotation(注解)注解

​ 代码分析:通过代码里标识的元数据(注解)对代码进行分析(使用反射)

​ 编译检查:通过代码里标识的元数据(注解)让编译器能够实现基本的编译检查(Override)

2.JDK中预定义的一些注解

​ @Override:检查被该注解标注的方法是否是继承自父类(接口)的

​ @Deprecated:将该注解标注的内容表示为已过时

​ @SuppressWarnings:压制警告 一般传递参数all @SuppressWarnings(“all”)

package annotation;
//@SuppressWarnings("all") //idea右侧上的黄线警告消除
public class AnnoDemo2 {
    @Override
    public  String toString() {
        return "666";
    }

    @Deprecated
    public static void show(){
        System.out.println("过时!");
    }

    @SuppressWarnings("all")
    public void newshow(){

    }

    public static void main(String[] args) {
        show();//过时方法,划了横线
    }
}
           

3.自定义注解

​ 3.1格式:

元注解

	public @interface 注解名称{
		属性列表;
	}
           

​ 3.2本质:在dos窗口下对下面代码进行编译生成字节码文件,然后利用 javap MyAnnotation.class反编译这个字节码文件得到其本质

public @interface MyAnnotation {
}
           

​ 即:注解本质上就是一个接口,该接口默认继承java.long.annotation.Annotation接口

​ 3.3 属性:接口中的抽象方法

​ 要求:

​ 3.3.1 属性的返回值类型:基本数据类型、String、枚举、注解、以上数据类型的数组

package annotation;

public @interface MyAnnotation {
    int shwo1();
    String show2();//称这个抽象方法就为注解的属性
    Person p1();//枚举类型
    MyAnnotation2 anno2();//注解类型
    String[] strs();
}
           

​ 3.3.2 定义了属性,在使用时需要给属性赋值

​ 3.3.2.1如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值否则必须在注解时赋值,多个属性赋值用逗号隔开

public @interface MyAnnotation {
    int show1();
    String show2() default "666";//称这个抽象方法就为注解的属性
    MyAnnotation2 anno2();
    String[] strs();
}
           
@MyAnnotation(show1 = 1,anno2 = @MyAnno2,strs={"abc","bbb"})//定义了必须赋值或者默认
public class AnnoDemo3 {
}
           

​ 3.3.2.2如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可

public @interface MyAnnotation {
    int value();
}
           
@MyAnnotation(1) //写value不写都可以
public class AnnoDemo3 {
}
           

​ 3.3.2.3数组赋值时,值使用{ }包裹,如果数组中只有一个值,则{ }可以省略

4.元注解:用于描述注解的注解

​ @Target:描述注解能够作用的位置

​ ElementType的取值:

​ TYPE:可以作用于类上

​ METHOD:可以作用于方法上

​ FIELD:可以作用于成员变量上

@Target(value = {ElementType.TYPE})//表示该AnnoDemo3注解只能作用于类上
public @interface AnnoDemo3 {
}

@AnnoDemo3//不报错
class test{
    @AnnoDemo3//报错
    public void show(){
        System.out.println("666");
    }
}
           

​ @Retention:描述注解被保留的阶段

​ @Retention(RetentionPolicy.SOURCE):当前被描述的注解

​ @Retention(RetentionPolicy.CLASS):当前被描述的注解,会被保留到class字节码文件中

​ @Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会被保留到class字节码文件中,并被JVM读到

@Retention(RetentionPolicy.RUNTIME)
public @interface AnnoDemo3 {
}
           

​ @Documented:描述注解是否被抽取到api文档中

​ @Inherited:描述注解是否被子类继承

5.在程序中使用(解析)注解:获取注解中定义的属性值

  1. 获取注解定义位置的对象 (Class、Method、Field)
  2. 获取指定的注解:getAnnotation(Class)
  3. 调用注解中的抽象方法获取配置的属性值(即注解中定义的抽象方法)
package annotation;

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

/*
框架类
 要求:在不能改变该类的任意代码下,可以创建任意类的对象,可以执行任意方法
         实现:1.配置文件 2.反射
         步骤:
            1.将需要创建的对象的全类名和需要执行的方法定义在配置文件中
            2.在程序中加载读取配置文件
            3.使用反射技术加载类文件进内存
            4.创建对象
            5.执行方法
 */

@Pro(className = "annotation.Demo2",methodName = "show")
public class ReflectTest {
    public static void main(String[] args) throws Exception{
        //1.解析注解
        //1.1获取该类的字节码文件对象
        Class<ReflectTest> reflectTestClass = ReflectTest.class;
        //2.获取上边的注解对象
        //其实就是在内存中生成了一个该注解接口的子类实现对象
        /*
        public class zilei implements Pro{
            public String className(){
                return "annotation.Demo2";
            }
            public String methodName(){
                return "show";
            }
        }
         */
        Pro an = reflectTestClass.getAnnotation(Pro.class);
        //3.调用注解对象中定义的抽象方法(属性),获取返回值
        String className = an.className();
        String methodName = an.methodName();
        //4.加载该类进内存
        Class cls = Class.forName(className);
        //5.创建对象
        Object obj = cls.newInstance();
        //6.获取方法对象
        Method method = cls.getMethod(methodName);
        //7.执行方法
        method.invoke(obj);
    }
}
           
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Pro {
    String className();
    String methodName();
}
           
public class Demo1 {
    public void show(){
        System.out.println("demo1---show()");
    }
}
public class Demo2 {
    public void show(){
        System.out.println("demo2---show()");
    }
}
           

6.小案例—实现测试功能

check.java

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

}
           

Calculator.java

package annotation.demo;
//简单的计算器类
public class Calculator {
    //加法
    @check
    public void add(){
        System.out.println(1+0);
    }
    //减法
    @check
    public void jianfa(){
        System.out.println(1-0);
    }
    //乘法
    @check
    public void mul(){
        System.out.println(1*0);
    }
    //除法
    @check
    public void div(){
        System.out.println(1/0);
    }
}

           

TestCheck.java

package annotation.demo;

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

/*
测试框架

当主方法执行后,会自动执行被检测的所有方法(即加check注解的),判断方法是否能被正确执行
 */
public class TestCheck {
    public static void main(String[] args) throws Exception{
        //1.创建计算器对象
        Calculator c = new Calculator();
        //2.获取字节码文件对象
        Class cls = c.getClass();
        //3.获取所有方法
        Method[] methods = cls.getMethods();
        int number = 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.捕获异常
                    //记录到文件中
                    number++;
                    bw.write(method.getName()+"方法出异常了");
                    bw.newLine();
                    bw.write("异常的名称:"+e.getCause().getClass().getSimpleName());
                    bw.newLine();
                    bw.write("异常的原因:"+e.getMessage());
                    bw.newLine();
                    bw.write("----------------");
                    bw.newLine();
                }
            }
        }
        bw.write("本次共出现了"+number+"次异常");
        bw.flush();
        bw.close();
    }
}
           

结果:

Annotation(注解)注解

7.注解的用途

  1. 用于让编译器知道
  2. 给解析程序用

继续阅读