注解

这里主要涉及的是四个基础的元注解(meta-annotation)

Java中的注解,有四个基本的元注解target Retention Documented Inherited 这四个元注解DocumentedInherited使用不多,主要为target retention两个注解

Target

描述注解的使用范围(即被修饰的注解可以用在什么地方).

public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value();
}

使用的范围也就是ElementType[]中的枚举类型数据

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
}

Retention

描述注解保留的时间范围(即:被描述的注解在它所修饰的类中可以被保留到何时).

Reteniton注解用来限定那些被它所注解的注解类在注解到其他类上以后,可被保留到何时,一共有三种策略

public enum RetentionPolicy {
    // 源文件保留
    SOURCE,
    // 编译期保留,默认值
    CLASS,
    // 运行期保留,可通过反射去获取注解信息
    RUNTIME
}

反射

正常方式: 引入需要的包类名称 —-> 通过 new实例化 —-> 获取实例化对象

反射: 实例化对象 —-> getClass()方法 —-> 得到完整的包类名称

通过反射, 使得Java具有类似动态语言的特性

反射所提供的功能

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

反射优点和缺点

优点:

可以动态创建和编译,有很大的灵活性

缺点:

慢于直接执行相同的操作

获取Class类的实例

  1. 已知某个类的实例

    User user = new User();
    Class<? extends User> aClass1 = user.getClass();
    
  2. 已知具体的类

    Class<User> aClass2 = User.class;
    
  3. 已知类的全名称

    Class<?> aClass3 = Class.forName("com.xxx.xxx");
    

类的加载过程

|—————————|          |——————————|           |————————————|
| 类的加载 | ------>  | 类的链接   |  -------> | 类的初始化  |
|   Load  | ----->   |   Link   |  -------> | Initialize |
|—————————|          |——————————|           |————————————|

获取Class对象之后可以做写什么

  1. 创建类的对象

    aClass.newInstance(); // 本质是调用了无参构造器
    

    若是没有无参构造器,可以通过明确调用对应的构造器,然后将参数传递进去即可

    aClass.getDeclaredConstrutor(Class...parameterTypes)
    
  2. 可以调用类中的方法

     User user = (User) aClass.newInstance();
     Method setName = aClass.getDeclaredMethod("setName", String.class);
     // invoke: 激活
     // (对象, "方法的值")
     setName.invoke(user, "维坤坤");
     System.out.println(user.getName());
    
  3. 操作类中的属性

     //反射操作属性
     User user3 = (User) aClass.newInstance();
     Field name = aClass.getDeclaredField("name");
     // 不能操作私有属性 需要关闭安全检测
     name.setAccessible(true);
     name.set(user3, "孔维坤");
     System.out.println(user3.getName());
    

注解和反射的实际应用

编写一个注解,适用于成员方法,然后运行保留

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AAAA {
    String value() default "";
}

通过反射获取注解中的内容

public class ReAno {
    @AAAA("我是方法注解")
    public void say(int a){
        System.out.println("wtf " + a);
    }

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        // 通过反射获取类
        Class<ReAno> reAnoClass = ReAno.class;
        Method say = reAnoClass.getDeclaredMethod("say", int.class);
        AAAA annotation = say.getAnnotation(AAAA.class);
        System.out.println(annotation.value());
        // 输出: 我是方法注解


    }
}

总结

通过反射获取注解的方式就是注解应用在什么范围, 就先通过反射获取对应的方法、或者成员变量,然后获取其注解即可。