Java annotations in

Notes Overview

Annotation (Annotation), also known as metadata. Describe a code level. It is a feature of JDK1.5 and later introduced the classes, interfaces, enumerations are on the same level. It can be declared in front of the packages, classes, fields, methods, local variables, parameters and the like of the method for these elements will be described, the comment.

 

Simply put, the program's instructions. To the computer to read. Click here to say Note: The procedure described in words. Programmers to read.

 

The role of classification

  1. Writing documentation: annotated code generated by the document identifier generated document doc [documentation]
  2. Code analysis: analyzed by annotating the code identity code [] using reflection

  3. Compile check: by annotating code identified so that the compiler can compile basic checking [Override]

 

JDK predefined number of notes

@Override

Detecting whether the method is marked annotations are inherited from the parent class (interface)

In the embodiment, the class inherits the parent class Object class, override the toString () method, marked with @Override annotations. The toString1 () method instead of rewriting it inherits the parent class, super class or interface method, marked with @Override notes, compile errors (Method does not override method from its superclass) will appear.

 

@Deprecated

The notes marked content, expressed obsolete

Creating a method method1, but later found to be a better way, can realize the function method1 method, it created a method method2. However, in order method1 method can use, they were marked with @Deprecated notes, marking the method is outdated, if we continue to call method1 method, as shown above, a horizontal line appears method.

 

@SuppressWarnings

Suppress warning

 

 

表示在注释元素(以及注释元素中包含的所有程序元素)中应该抑制命名的编译器警告。 请注意,给定元素中抑制的一组警告是所有包含元素中抑制的警告的超集。 例如,如果您注释一个类来抑制一个警告并注释方法来抑制另一个警告,则两个警告将在该方法中被抑制。一般传递参数all —— @SuppressWarnings("all")

 

自定义注解:格式和本质

格式

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

 

本质

定义一个注解:

public @interface MyAnnotation {
    /*
    属性:接口中的抽象方法
     */
    public  abstract String method();

}

注解本质上就是一个接口,MyAnnotation接口默认继承Annotation接口。接口中可以定义的成员方法是接口的属性。

public interface MyAnnotation extends java.lang.annotation.Annotation {}

public interface Annotation 接口,是所有注释类型扩展的公共接口。

 

自定义注解:属性定义

属性

接口中的抽象方法。

 

要求

  1. 属性的返回值类型有下列取值

    • 基本数据类型
    • String
    • 枚举
      public enum Person {
      P1, P2 }
    • 注解
      public @interface MyAnnotation2 {
      }
    • 以上数据类型的数组

      使用的时候:


  2. 定义了属性,在使用时需要给属性赋值
    如:定义一个注解

    在使用的时候:


    • 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
      如:定义一个注解

      在使用的时候:
      1、使用默认值:"LeeHua"

      2、不使用默认值:

       

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

      使用的时候:可以省略value,也可以不省略value,如下




    • 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略
      如:定义一个注解

      使用的时候,{}可以省略,也可以不省略:




元注解

简单来说,元注解就是描述注解的注解。如@Override

这里的@Target和@Retention就是元注解。

 

四个元注解

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

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

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

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

 

 @Target

public @interface Target {
    /**
     * 返回注释类型可应用于的元素种类的数组。
     */
    ElementType[] value();
}

ElementType[] 是一个枚举数组:

/**
 * 这个枚举类型的常量提供了在Java程序中可能出现注释的句法位置的简单分类。 
 * 在java.lang.annotation.Target元注释中使用这些常量来指定写入给定类型的
 * 注释的合法位置。
 */
public enum ElementType {
    /** 类,接口(包括注释类型)或枚举声明 */
    TYPE,

    /** 字段声明(包括枚举常数) */
    FIELD,

    /** 方法声明 */
    METHOD,

    /** 形式参数声明 */
    PARAMETER,

    /** 构造函数声明 */
    CONSTRUCTOR,

    /** 局部变量声明 */
    LOCAL_VARIABLE,

    /** 注释类型声明 */
    ANNOTATION_TYPE,

    /** 包声明 */
    PACKAGE,

    /**
     * 类型参数声明
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * 使用类型
     *
     * @since 1.8
     */
    TYPE_USE
}

 

举例

自定义一个注解:

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

@Target(ElementType.TYPE) // 表示MyAnnotation7注解只能作用于类上
public @interface MyAnnotation7 {

}

该注解的使用:

在这里,MyAnnotation7注解作用在类上是可以的。不过,作用在方法上,或成员变量上、方法上、... ... ,是不可以的。

假如想要作用于成员变量上、方法上、... ... ,可以根据public enum ElementType { ... ... } 在调用元注解的注解上进行添参,从而实现更多的作用域,如:

import java.lang.annotation.ElementType;

@Target(ElementType.TYPE, , ElementType.METHOD, ElementType.FIELD) 
// 表示MyAnnotation7注解能作用于类、方法、成员变量上
public @interface MyAnnotation7 {

}

 

@Retention

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * @return 保留策略。
     */
    RetentionPolicy value();
}

RetentionPolicy[] 是一个枚举数组:

/**
 * 注释保留策略。 此枚举类型的常量描述了用于保留注释的各种策略。 它们
 * 与@Retention元注释类型一起使用,以指定将保留注释多长时间。
 */
public enum RetentionPolicy {
    /** 注释将被编译器丢弃。 */
    SOURCE,

    /** 注释将由编译器记录在类文件中,但JVM不需要在运行时保留。 */
    CLASS,

    /**
     * 注释将由编译器记录在类文件中,并由JVM在运行时保留,因此可以反射读取。
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}

 

举例

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

@Retention(RetentionPolicy.RUNTIME) 
// 当前被描述的注解(MyAnnotation8),会保留到class字节码文件中,并被JVM读取到 public @interface MyAnnotation8 { }

 

@Documented

/**
 * 表示具有类型的注释默认情况下由javadoc和类似工具记录。
 * 即:描述注解是否被抽取到api文档中
 */ 
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

 

举例

自定义一个注解:

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

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
// 表示MyAnnotation7注解能作用于类、方法、成员变量上
@Retention(RetentionPolicy.RUNTIME)
// 当前被描述的注解(MyAnnotation8),会保留到class字节码文件中,并被JVM读取到
@Documented
// 描述注解会被抽取到api文档中 public @interface MyAnnotation9 { }

使用该注解:

@MyAnnotation9
public class Demo09Annotation {

    private String name = "LeeHua";

    @MyAnnotation9
    public void method() {

    }
}

在终端,进行编译:

cd /Users/liyihua/IdeaProjects/Study/src/view/study/demo47/
javadoc Demo09Annotation.java

javadoc编译完成后,生成一大堆文件:

 

 

 

 

 

 

 

 Google浏览器打开index-all.html文件,可以查看被抽取到API文档中的描述:

 

 

@Inherited

/** 描述注解是否被子类继承 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

 

举例

自定义一个注解:

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

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
// 表示MyAnnotation7注解能作用于类、方法、成员变量上
@Retention(RetentionPolicy.RUNTIME)
// 当前被描述的注解(MyAnnotation8),会保留到class字节码文件中,并被JVM读取到
@Documented
// 描述注解会被抽取到api文档中
@Inherited
// 描述注解是否被子类继承 public @interface MyAnnotation10 { }

父类使用这个注解:

@MyAnnotation10
public class Demo09Annotation {

}

子类继承父类:

public class Demo10Annotation extends Demo09Annotation {

}

在@Inherited注解描述的情况下,父类使用了@MyAnnotation10注解,子类继承父类,也会使用@MyAnnotation10注解。

 

案例一

需求

写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法

 

实现之前,定义Person.java、Student.java:

package view.study.demo48;

public class Person {
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public void personMethod() {
        System.out.println("我是Person中的方法!!!");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
package view.study.demo48;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public void studentMethod() {
        System.out.println("我是Student中的方法!!!");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

 

实现

定义一个注解:

package view.study.demo48;

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) // 当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
public @interface pro {

    public abstract String className();

    public abstract String methodName();

}

定义实现类:

package view.study.demo48;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
 * 利用:反射、注解实现
 */
@pro(className = "view.study.demo48.Person", methodName = "personMethod")
public class Demo01Reflection {

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException,
            InstantiationException, NoSuchMethodException, InvocationTargetException {
        // 1. 解析注解
        // 1.1 获取Demo01Reflection类的字节码文件对象
        Class<Demo01Reflection> drClass = Demo01Reflection.class;

        // 2. 获取上边的注解对象:@pro(className = "view.study.demo48.Person", methodName = "personMethod")
        pro annotation = drClass.getAnnotation(pro.class);

        // 3. 调用注解中定义的抽象方法,获取返回值
        // 3.1 获取className
        String className = annotation.className();
        // 3.2 获取methodName
        String methodName = annotation.methodName();

        // 4. 加载该类(获取到的类)进内存
        Class<?> aClass = Class.forName(className);

        // 5. 创建对象
        Object object = aClass.newInstance();

        // 6. 获取方法对象
        Method method = aClass.getMethod(methodName);

        // 7. 执行方法
        method.invoke(object);
    }

}

第二步:

// 第二步其实就是在内存中生成了一个该注解接口的子类实现对象
public class proImpl implements pro {

    // 实现pro接口中的className()方法和methodName()方法
    public String className() {
        return className;
    }

    public String methodName() {
        return methodName;
    }

}

运行程序,控制台输出:

我是Person中的方法!!!

修改注解中的className、methodName:

@pro(className = "view.study.demo48.Student", methodName = "studentMethod")

再次运行程序,控制台输出:

我是Student中的方法!!!

 

案例二

需求

利用反射、注解,写一个简单的测试框架,当主方法执行后,会自动加载被检测的所有方法,判断是否有异常,并记录到文件中。注解名称是@Check

被测试的方法如下:

package view.study.demo48;

public class Calculator {

    /** 加法 */
    @Check
    public int add(int a, int b) {
        return a + b;
    }

    /** 减法 */
    @Check
    public int sub(int a, int b) {
        return a - b;
    }

    /** 乘法 */
    @Check
    public int mul(int a, int b) {
        return a * b;
    }

    /** 除法 */
    @Check
    public int div(int a, int b) {
        return a / b;
    }

    public void method() {
        System.out.println("永无bug!!!");
    }

}

 

实现

自定义一个注解:

package view.study.demo48;

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

@Retention(RetentionPolicy.RUNTIME) // 当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
@Target(ElementType.METHOD) // 注解能作用于方法
public @interface Check {
}

主方法实现类:

package view.study.demo48;

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

public class Demo01Calculator {

    public static void main(String[] args) throws IOException {
        // 1. 创建计算器对象
        Calculator calculator = new Calculator();

        // 2. 通过计算器对象,获取字节码文件
        Class<? extends Calculator> aClass = calculator.getClass();

        // 3. 通过字节码文件,获取calculator中所有的方法
        Method[] methods = aClass.getDeclaredMethods();

        // 4. 定义异常出现的次数、创建字符缓冲输入流
        int count = 0;
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("bug.txt", true));

        // 5. 判断方法上是否有"@Check"注解
        for (Method method : methods) {
            if (method.isAnnotationPresent(Check.class)) {
                try {

                    // 6. 如果方法上有"@Check"注解,那么执行该方法
                    method.invoke(calculator, 1, 0);
                } catch (Exception e) {
                    // 7. 捕获异常,记录到文件中
                    count ++;
                    bufferedWriter.newLine();
                    bufferedWriter.write("被测试的方法:" + method.getName() + ",该方法出现异常。");
                    bufferedWriter.newLine();
                    bufferedWriter.write("异常的名称:" + e.getCause().getClass().getSimpleName());
                    bufferedWriter.newLine();
                    bufferedWriter.write("异常的原因:" + e.getCause().getMessage());
                    bufferedWriter.newLine();
                }
            }
        }
        bufferedWriter.write("本次测试一共出现了" + count + "次异常。");
        bufferedWriter.newLine();
        bufferedWriter.write("====================================");

        // 8. 释放BufferedWriter资源
        bufferedWriter.flush();
        bufferedWriter.close();
    }

}

运行主方法,bug.txt文件中会写入一定的内容,内容如下:


被测试的方法:div,该方法出现异常。 异常的名称:ArithmeticException 异常的原因:
/ by zero 本次测试一共出现了1次异常。 ====================================

分析:

要被检测的方法有四个,分别是:加法、减法、乘法、除法。

主方法中传入的参数a、b是:1和0

很明显,1除0,是无意义的,所以div方法出现异常,异常名称是:ArithmeticException

其他方法没有出现异常。所以出现的异常次数是:1

 

Guess you like

Origin www.cnblogs.com/liyihua/p/12305369.html