注解的使用

jdk5.0开始出现注解。Java 语言中的类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。

使用"name=value"保存信息

框架 = 注解 + 反射 + 设计模式

常见注解

文档中的注解

@author 标明开发该类模块的作者,多个作者之间使用,分割
@version 标明该类模块的版本
@see 参考转向,也就是相关主题
@since 从哪个版本开始增加的
@param 对方法中某参数的说明,如果没有参数就不能写
@return 对方法返回值的说明,如果方法的返回值类型是void就不能写
@exception 对方法可能抛出的异常进行说明 ,如果方法没有用throws显式抛出的异常就不能写

其中
@param @return 和 @exception 这三个标记都是只用于方法的。
@param的格式要求:@param 形参名 形参类型 形参说明
@return 的格式要求:@return 返回值类型 返回值说明
@exception的格式要求:@exception 异常类型 异常说明
@param和@exception可以并列多个

编译时的注解

@Override: 限定重写父类方法, 该注解只能用于方法
@Deprecated: 用于表示所修饰的元素(类, 方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择
@SuppressWarnings: 抑制编译器警告

自定义注解

  • 定义新的 Annotation 类型使用 @interface 关键字
  • 自定义注解自动继承了java.lang.annotation.Annotation接口
  • Annotation 的成员变量在 Annotation 定义中以无参数方法的形式来声明。其方法名和返回值定义了该成员的名字和类型。我们称为配置参数。类型只能是八种基本数据类型、String类型、Class类型、enum类型、Annotation类型、以上所有类型的数组。
  • 可以在定义 Annotation 的成员变量时为其指定初始值, 指定成员变量的初始值可使用 default 关键字
  • 如果只有一个参数成员,建议使用参数名为value
  • 如果定义的注解含有配置参数,那么使用时必须指定参数值,除非它有默认值。格式是“参数名 = 参数值”,如果只有一个参数成员,且名称为value,可以省略“value=”
  • 没有成员定义的 Annotation 称为标记(例@Override); 包含成员变量的Annotation 称为元数据 Annotation

jdk中四个元注解

  • Retention
  • Target
  • Documented
  • Inherited

Retention

@Retention: 只能用于修饰一个 Annotation 定义, 用于指定该 Annotation 的生命周期,@Rentention 包含一个 RetentionPolicy 类型的成员变量, 使用@Rentention 时必须为该 value 成员变量指定值:

  • RetentionPolicy.SOURCE:在源文件中有效(即源文件保留),编译器直接丢弃这种策略的
    注释
  • RetentionPolicy.CLASS:在class文件中有效(即class保留) , 当运行 Java 程序时, JVM
    不会保留注解。 这是默认值
  • RetentionPolicy.RUNTIME:在运行时有效(即运行时保留),当运行 Java 程序时, JVM 会
    保留注释。程序可以通过反射获取该注释。
package java.lang.annotation;

/**
 * Indicates how long annotations with the annotated type are to
 * be retained.  If no Retention annotation is present on
 * an annotation type declaration, the retention policy defaults to
 * {@code RetentionPolicy.CLASS}.
 *
 * <p>A Retention meta-annotation has effect only if the
 * meta-annotated type is used directly for annotation.  It has no
 * effect if the meta-annotated type is used as a member type in
 * another annotation type.
 *
 * @author  Joshua Bloch
 * @since 1.5
 * @jls 9.6.3.2 @Retention
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

//  生命周期的枚举类 
package java.lang.annotation;

/**
 * Annotation retention policy.  The constants of this enumerated type
 * describe the various policies for retaining annotations.  They are used
 * in conjunction with the {@link Retention} meta-annotation type to specify
 * how long annotations are to be retained.
 *
 * @author  Joshua Bloch
 * @since 1.5
 */
public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}

Target

@Target: 用于修饰 Annotation 定义, 用于指定被修饰的 Annotation 能用于修饰哪些程序元素。
@Target 也包含一个名为 value 的成员变量。

可修饰结构的枚举类

package java.lang.annotation;

/**
 */
public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,
    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

Documented

  • @Documented: 用于指定被该元 Annotation 修饰的 Annotation 类将被javadoc 工具提取成文档。默认情况下,javadoc是不包括注解的。
  • 定义为Documented的注解必须设置Retention值为RUNTIME

Inherited

@Inherited: 被它修饰的 Annotation 将具有继承性。如果某个类使用了被@Inherited 修饰的 Annotation, 则其子类将自动具有该注解。

  • 比如:如果把标有@Inherited注解的自定义的注解标注在类级别上,子类则可以
    继承父类类级别的注解
  • 实际应用中,使用较少

jdk8注解中新特性

  • 可重复的注解
  • 用于类型的注解

可重复的注解

jdk8之前:新建一个注解,成员变量为要注解的数组,使用新建的注解内多个value对。

jdk8:原注解中加@Repeatable(新注解.class),这样在使用原注解的时候可以注释多个相同的注解(而不是使用多个value)

用于类型的注解

TYPE_PARAMETER:标识该注解可以写在类型变量的声明语句中(如泛型中)

public class TestTypeDefine<@TypeDefine() U> {
    private U u;
    public <@TypeDefine() T> void test(T t){
    } }

    @Target({ElementType.TYPE_PARAMETER})
    @interface TypeDefine{ }

TYPE_USE:可以修饰在使用类型的任何语句中

@MyAnnotation
public class AnnotationTest<U> {
    @MyAnnotation
    private String name;
    public static void main(String[] args) {
    AnnotationTest<@MyAnnotation String> t = null;
    int a = (@MyAnnotation int) 2L;
    @MyAnnotation
    int b = 10;
    }
    public static <@MyAnnotation T> void method(T t) { }

public static void test(@MyAnnotation String arg)throws @MyAnnotation Exception { } }

@Target(ElementType.TYPE_USE)
@interface MyAnnotation { }

准备的Person类

package com.example.annotationAndReflex;

import org.jetbrains.annotations.NotNull;

/**
 * @author :yhc
 * @date :2021/8/29
 **/
@MyAnnotation(value = "hi")
public class Person extends Creature<String>
        implements Comparable<String>,MyInterface{
    private String name;
    int age;
    public int id;

    public Person() {
    }

    @MyAnnotation(value = "abc")
    public Person(String name){
        this.name = name;
    }

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

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



    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;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @MyAnnotation(value = "hello")
    private String show(String nation){
        System.out.println("我的国籍是" + nation);
        return nation;
    }

    public String display(String interests) throws NullPointerException{
        return interests;
    }

    public void show(){
        System.out.println("我是example的show方法");
    }

    private String showPrivate(String word){
        System.out.println("传进来的word是" + word);
        return word;
    }


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

    @Override
    public int compareTo(@NotNull String o) {
        return 0;
    }

    @Override
    public void info() {
        System.out.println("我是一个人");
    }
}

反射的使用

反射:可以实现在运行时可以知道任意一个类的属性和方法。

反射的功能

  • 运行时判断一个对象所属的类
  • 运行时构造任意一个类的对象
  • 运行时判断任意一个类所具有的成员变量和方法
  • 运行时获取泛型信息
  • 运行时调用任意一个对象的成员变量和方法
  • 运行时处理注解
  • 生成动态代理

反射相关的API

  • java.lang.Class:代表一个类
  • java.lang.reflect.Method:代表类的方法
  • java.lang.reflect.Field:代表类的成员变量
  • java.lang.reflect.Constructor:代表类的构造器
  • java.lang.reflect.Modifier:代表修饰符

通过类获取Class对象来新建对象

package com.example.annotationAndReflex;

import org.junit.jupiter.api.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * @author :yhc
 * @date :2021/8/29
 **/
public class ReflexTest {


    @Test
    public void reflexTest1() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        // 通过反射创建对象
        Class pClass = Person.class;

        Constructor constructor = pClass.getConstructor(String.class, int.class);
        Object obj = constructor.newInstance("实例name", 15);
        System.out.println(obj);

        Person p = (Person) obj;
        System.out.println(p);
        System.out.println("---------------------------");
        // 通过反射调用对象指定属性、方法
        // 调用属性
        Field age = pClass.getDeclaredField("age");
        age.set(p,10);
        System.out.println(p);
        // 调用方法(空参)
        Method show = pClass.getDeclaredMethod("show");
        show.invoke(p);
    }

    @Test
    public void reflexTest2() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        // 通过反射获取私有的构造器、方法、属性。
        Class pClass = Person.class;
        Constructor pCon = pClass.getDeclaredConstructor(String.class);
        pCon.setAccessible(true);
        Person p = (Person) pCon.newInstance("Tom");
        System.out.println(p);

        // 调用私有属性
        Field name = pClass.getDeclaredField("name");
        name.setAccessible(true);
        name.set(p,"Jerry");
        System.out.println(p);

        // 调用私有的方法
        Method show = pClass.getDeclaredMethod("showPrivate",String.class);
        show.setAccessible(true);
        String s = (String) show.invoke(p,"!!word!!");
        System.out.println(s);
    }


}

Class实例对应着一个运行时类

获取Class对象的4中方式

// 四种获取Class实例的方式
@Test
public void reflexTest3() throws ClassNotFoundException {
    //方式一: 调用运行时类的属性
    Class<Person> personClass = Person.class;
    //方式二: 通过运行时类的对象
    Person person = new Person();
    Class personClass2 = person.getClass();
    //方式三: 通过Class的静态方法: forName(String classPath)  类的全类名
    Class personClass3 = Class.forName("com.example.annotationAndReflex.Person");
    //方式四: 使用类的加载器 ClassLoader
    ClassLoader classLoader = ReflexTest.class.getClassLoader();
    Class personClass4 = classLoader.loadClass("com.example.annotationAndReflex.Person");

    System.out.println(personClass == personClass2);
}

Class可以是哪些结构

  • Class c1 = Object.class;
  • Class c2 = Comparable.class;
  • Class c3 = String[].class;
  • Class c4 = int[][].class;
  • Class c5 = ElementType.class;
  • Class c6 = Override.class;
  • Class c7 = int.class;
  • Class c8 = void.class;
  • Class c9 = Class.class;

ClassLoader

//认识ClassLoader
@Test
public void reflexTest4(){
    ClassLoader classLoader = ReflexTest.class.getClassLoader();
    System.out.println(classLoader);
    //sun.misc.Launcher$AppClassLoader@dad5dc 系统类加载器
    System.out.println(classLoader.getParent());
    // sun.misc.Launcher$ExtClassLoader@d46ca6 扩展类加载器
    // getParent()无法获取Bootstrap ClassLoader 引导类加载器(加载核心类库)
    ClassLoader bootstrapClassLoader = String.class.getClassLoader();
    System.out.println(bootstrapClassLoader); // null 无法获取
}
  • 引导类加载器:用C++编写的,是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取
  • 扩展类加载器:负责jre/lib/ext目录下的jar包或 –D java.ext.dirs 指定目录下的jar包装入工作库
  • 系统类加载器:负责java –classpath 或 –D java.class.path所指的目录下的类与jar包装入工作 ,是最常用的加载器
// 使用Properties读取文件
@Test
public void reflexTest5() throws IOException {
    Properties pros = new Properties();
    FileInputStream fis = new FileInputStream("src/test/java/com/example/annotationAndReflex/jdbc.properties");
//        pros.load(fis);
//        String user = pros.getProperty("user");
//        String password = pros.getProperty("password");
//        System.out.println("账号: " + user + "\n密码: " + password);

    ClassLoader classLoader = ReflexTest.class.getClassLoader();
    // 获取输入流 默认相对路径从src下
    InputStream is = classLoader.getResourceAsStream("com/example/annotationAndReflex/jdbc.properties");
    pros.load(is);
    String user = pros.getProperty("user");
    String password = pros.getProperty("password");
    System.out.println("账号: " + user + "\n密码: " + password);
}


@Test
public void reflexTest6() throws InstantiationException, IllegalAccessException {
    Class personClass = Person.class;
    Object o = (Person)personClass.newInstance(); // 默认调用非private空参构造器(无则报异常)
    System.out.println(o);
}

动态决定创建对象(反射的动态性)

/**
    * 创建一个指定类的对象
    * @param classPath
    * @return
    * @throws ClassNotFoundException
    * @throws InstantiationException
    * @throws IllegalAccessException
    */
@Test
public Object reflexCreateObj(String classPath) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
    Class objClass = Class.forName(classPath);
    return objClass.newInstance();
}

// 创建对应运行时的对象
@Test
public void reflexTest7() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
    int num = new Random().nextInt(3);
    String classPath = "";
    switch (num){
        case 0:
            classPath = "java.util.Date";
            break;
        case 1:
            classPath = "java.lang.Object";
            break;
        case 2:
            classPath = "com.example.annotationAndReflex.Person";
            break;
    }
    System.out.println(num);
    Object o = reflexCreateObj(classPath);
    System.out.println(o);
}

获取运行时类Person的完整结构

// 获取运行时类Person的完整结构
    // 获取当前类的属性
    @Test
    public void reflexTest8(){

        Class personClass = Person.class;
        // 获取属性结构(只能拿到public的) 父类的也可以拿到
        Field[] fields = personClass.getFields();
        for (Field f : fields){
            System.out.println(f);
        }
        System.out.println("-----------------------");
        // 获取声明过的属性(获取所有权限的属性) 父类属性无法拿到
        Field[] declaredFields = personClass.getDeclaredFields();
        for (Field f : declaredFields) {
            System.out.println(f);
        }
    }
    //获取当前类的  权限修饰符 数据类型 变量名
    @Test
    public void reflexTestFiled(){
        Class personClass = Person.class;
        Field[] declaredFields = personClass.getDeclaredFields();
        for (Field f: declaredFields) {
            // 1.权限修饰符
            // 默认 - 0
            // public - 1
            // private - 2
            int modifiers = f.getModifiers();
            System.out.print(modifiers + "\t");
            System.out.print(Modifier.toString(modifiers) +  "\t");
            // 2.变量的数据类型
            Class type = f.getType();
            System.out.print(type + "\t");
            // 3.变量名
            String name = f.getName();
            System.out.print(name + "\t");
            System.out.println();
        }

    }

    //运行时方法测试
    @Test
    public void reflexTestMethod(){
        Class<Person> personClass = Person.class;
        // getMethods() 获取当前运行时类及其父类里的public方法
        Method[] methods = personClass.getMethods();
        for (Method m: methods) {
            System.out.println(m);
        }
        System.out.println("--------------------------------------------");
        // 获取当前运行时类所有(非父类)方法
        Method[] declaredMethods = personClass.getDeclaredMethods();
        for (Method m: declaredMethods) {
            System.out.println(m);
        }
    }
    //方法: 权限修饰符 返回值类型 形参列表
    @Test
    public void reflexTestMethodDetail(){
        Class<Person> personClass = Person.class;
        Method[] declaredMethods = personClass.getDeclaredMethods();
        for (Method m : declaredMethods) {
            // 1.获取方法声明的注解信息
            Annotation[] annos = m.getAnnotations();
            for (Annotation a: annos) {
                System.out.print(a + "\t");
            }
            // 2.权限修饰符
            System.out.print(Modifier.toString(m.getModifiers()) + "\t");
            // 3.返回值类型
            System.out.print(m.getReturnType().getName() + "\t");
            // 4.方法名
            System.out.print(m.getName() + "\t");
            // 5.形参列表
            System.out.print("----(");
            Class[] parameterTypes = m.getParameterTypes();
            if(!(parameterTypes == null && parameterTypes.length == 0)){
                for (int i = 0; i < parameterTypes.length; i++) {
                    System.out.print(parameterTypes[i].getName() + " args_ " + i);
                }
            }
            System.out.print(")");
            // 6.抛出的异常
            Class[] exceptionTypes = m.getExceptionTypes();
            if(!(exceptionTypes == null && exceptionTypes.length == 0)){
                for (int i = 0; i < exceptionTypes.length; i++) {
                    System.out.print(exceptionTypes[i] + ",");
                }
            }
            System.out.println();
        }
    }

    // 获取构造器结构
    @Test
    public void reflexTestConDetail(){
        Class<Person> personClass = Person.class;
        // 获取当前类的public构造器
        Constructor[] constructors = personClass.getConstructors();
        for (Constructor c: constructors) {
            System.out.println(c);
        }
        System.out.println("----------------------------");

        // 获取当前类所有构造器
        Constructor[] declaredConstructors = personClass.getDeclaredConstructors();
        for (Constructor c: declaredConstructors) {
            System.out.println(c);
        }
    }

    // 获取运行时的父类
    @Test
    public void reflexTestParent(){
        Class<Person> personClass = Person.class;
        // 不带泛型
        Class<? super Person> superclass = personClass.getSuperclass();
        System.out.println(superclass);
        // 带泛型
        Type genericSuperclass = personClass.getGenericSuperclass();
        System.out.println(genericSuperclass);
         //输出泛型
        ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
        System.out.println(actualTypeArguments[0]);
    }

    // 获取运行时的接口
    @Test
    public void reflexTestInterface(){
        Class<Person> personClass = Person.class;
        // 可能为多实现
        Class[] interfaces = personClass.getInterfaces();
        for (Class c: interfaces){
            System.out.println(c);
        }
        System.out.println("-----------------------");
        // 获取父类的接口
        Class[] superInterfaces = personClass.getSuperclass().getInterfaces();
        for (Class c: superInterfaces) {
            System.out.println(c);
        }
    }

    // 获取运行时类的包
    @Test
    public void packageTest(){
        Class<Person> personClass = Person.class;
        Package aPackage = personClass.getPackage();
        System.out.println(aPackage);
    }

    // 获取运行时类的注解
    @Test
    public void getAnnotation(){
        Class<Person> personClass = Person.class;
        Annotation[] annotations = personClass.getAnnotations();
        for (Annotation annotation: annotations) {
            System.out.println(annotation);
        }
    }

调用运行时类的指定结构(属性、方法)

// 调用运行时类的指定结构
    // 获取、设置相应的属性
    @Test
    public void fieldTest() throws NoSuchFieldException, InstantiationException, IllegalAccessException {
        Class<Person> personClass = Person.class;

        // 获取对应对象
        Person person = personClass.newInstance();
        // 获取指定的属性:要求属性为public(通常不采用)
        Field id = personClass.getField("id");
        /**
         *  获取所有权限的属性
          */
        Field name = personClass.getDeclaredField("name");
        // 开启访问
        name.setAccessible(true);
        /**
         * set()
         * 参数一: 指明要设置的对象
         * 参数二: 指明要设置的数值
         */
        id.set(person,1001);
        name.set(person,"tom");
        /**
         * get(): 要获取的对象
         */
        int pid = (int) id.get(person);
        System.out.println(pid);
        System.out.println(name.get(person));
    }

       // 操作运行时类指定的方法
    @Test
    public void methodTest() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
        Class<Person> personClass = Person.class;
        // 创建对象
        Person person = personClass.newInstance();
        /**
         * 获取指定方法
         * 参数一: 方法名
         * 参数二: 方法的参数
         */
        Method showMethod = personClass.getDeclaredMethod("show",String.class);
        // 开启访问权限
        showMethod.setAccessible(true);
        /**
         * 传入对象调用方法
         * invoke()
         *  参数一:需要调用方法的对象
         *  参数二:传入调用方法的参数
         *  返回值 == 调用方法的返回值
         *
         *  调用静态方法传入 Person.class 作为参数一
          */
        String nation = (String) showMethod.invoke(person, "CHN");
        System.out.println(nation);
    }

    // 调用运行时类中指定的构造器
    @Test
    public void getConstructor() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class<Person> personClass = Person.class;
        /**
         * 指明参数列表
         */
        Constructor<Person> constructor = personClass.getConstructor(String.class, int.class);
        // 确保可访问
        constructor.setAccessible(true);
        Person person = constructor.newInstance("Tom", 11);
        System.out.println(person);
    }