注解与反射

注解是 Java 5 的一个新特性。注解是插入你代码中的一种注释或者说是一种元数据(meta data)。这些注解信息可以在编译期使用预编译工具进行处理(pre-compiler tools),也可以在运行期使用 Java 反射机制进行处理

一般情况下,注解是用来给程序员、编译器提示的,就比如 @NoNull 就是用来提示不为空,在null时,编译器会根据注解报出相关的异常。

 但是注释还有一个很重要的作用就是和反射一起使用,我们可以通过反射拿到被注释的字段、方法、类,当然也可以拿到注释中的信息。

 还是先说用来注解注解的注解(原生注解),哈哈哈!

@Target

 表示该注解可以用于什么地方,可能的ElementType参数有:

  • CONSTRUCTOR:构造器的声明
  • FIELD:域声明(包括enum实例)
  • LOCAL_VARIABLE:局部变量声明
  • METHOD:方法声明
  • PACKAGE:包声明
  • PARAMETER:参数声明
  • TYPE:类、接口(包括注解类型)或enum声明
@Retention

 表示需要在什么级别保存该注解信息。可选的RetentionPolicy参数包括:

  • SOURCE:注解将被编译器丢弃
  • CLASS:注解在class文件中可用,但会被VM丢弃
  • RUNTIME:VM将在运行期间保留注解,因此可以通过反射机制读取注解的信息。
@Document

 将注解包含在Javadoc中。

@Inherited

 允许子类继承父类中的注解。

 看完了若是一脸懵,请看注解的构造方式: 注解的使用:

 是不是感觉这里看不出来什么特殊含义。但是,假如说,对我自己,这个注解代表被注解的方法是用来干某些耗时操作的,那么是不是就有意义了?

 当然这样使用还是太浅层了,我们来结合反射试试!

反射

我们还是先不谈注解和反射搭配好伐?先单独谈谈反射:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。

首先定义了一个Student类,这个类是我们想象出来的(可以啥子都有),专门用来接下来实现反射用的。一般情况下,我们是在对这个类一点都不了解的情况下才会想着去获取它的信息,好奇心按捺不住了啊!!!

在另外的类里面获取Student的信息是这样实现的:

 其它的字段、方法、类的获取方式待自己慢慢探索吧。

 但是万一有的小伙伴手痒,又想使用它拿到的方法该怎么办呢?

 没关系,上热腾腾的代码:

是不是感受出来什么了?通过反射获取一个类的信息就是这么简单!

其实在字段或者方法上面加上注解也就是比普通的特殊了那么一丢丢,我们稍加判断同样是能够获取到被注解了的对象。

笔者手懒,就通通使用上面已经建好了的对象。

上代码!!!

先缓口气,我们迈出了很大一步,我们已经获取到了被注释了的方法。接下来就是按照我们自己的需求来写了。

假如,我只是要实现这个方法,那么就可以采用invoke()方法去实现啦。但是我要想知道注释里面的信息呢(例如上面的"小明",1)。

这也是超级简单的:

轻而易举实现啦。

总结

Java的注解和反射搭配使用是有很大作用的,这两者搭配是可以实现超大程度的解耦。我在这个类里面实现了注解,我可以在另外一个毫不相关的类里面去获得这个类的内容,并且实现里面的方法。

很多好的三方库的都是实现的注解和反射,例如Dagger2、Retrofit、ButterKnife...但是这些第三库不是这么简单去实现的,它们会生成工具类来快速实现信息获取。

但是在Java中实现反射可是一件“费劲”的事儿,是比较消耗CPU资源的。所以,请勿滥用反射!

  • 反射为我们提供了全面的分析类信息的能力,例如类的方法,成员变量和构造器等的相关信息,反射能够让我们很方便的获取这些信息, 而实现这个获取过程的关键是取得类的Class对象,然后根据Class对象取得相应的Method对象,Field对象和Constructor对象,再分别根据各自的API取得信息。
  • 反射还为我们提供动态加载类的能力、

一些细节

  • API中getDeclaredXXX和getXXX的区别在于前者只获取本类声明的XXX(如成员变量或方法),而不获取超类中继承的XXX, 后者相反

  • API中, getXXXs(注意后面的s)返回的是一个数组, 而对应的 getXXX("键")按键获取一个值(这个时候因为可能报已检查异常所以要用try-catch语句包裹)

  • 私有成员变量是不能直接获取到值的!因为java本身的保护机制,允许你取得私有成员变量的类型,但是不允许直接获取值,所以要对对应的field对象调用field.setAccessible(true) 放开权限

  • 课程内容梗概*

    1. Java反射机制概述
    2. 理解Class类并获取Class实例
    3. 类的加载 (包含Classloader)
    4. 创建运行时类的对象
    5. 获取运行时类的完整信息
    6. 调用运行时类

    Java是准动态语言

    因为它有反射机制, 才可以视之为"准动态语言"

    从内存角度理解反射

    类加载完毕后, 在堆内存的方法区就产生了一个唯一Class类型的对象, 其中包含了完整的类的结构信息. 我们可以通过它看到类的完整结构

    反射对性能有一定影响

    因为反射是一种"解释操作", 总是慢于"直接执行"

    Class类

    对Class类的认识

    • Class本身也是一个类
    • Class对象只能由系统建立
    • 一个类加载到JVM, 只会对应一个Class对象
    • 且对应一个.class文件
    • 每个类的实例都会记得自己是由哪个Class对象生成的
    • 通过Class对象可以完整得到这个类中所有的结构信息
    • Class类是Relection的根源, 任何想动态加载运行的类, 只能先获取其Class对象
    • 两个元素类型相同的数组, 即使size不同, 实际也是一个对象(hashCode相等. 同维度数组)

    复制

    // 数组
    int[] a = new int[10];
    int[] b = new int[100];
    // hashcode打印结果相等
    460141958
    460141958

    哪些可以拥有Class对象?

    • 普通类(包括内部类)
    • interface
    • []
    • enum
    • annotation
    • primitive type, 基本数据类型
    • void

    复制

    Class<Object> c1 = Object.class;            // 类
    Class<Comparable> c2 = Comparable.class;    // 接口
    Class<String[]> c3 = String[].class;        // 数组 class [Ljava.lang.String;
    Class<int[][]> c4 = int[][].class;          // 数组 class [[I
    Class<Override> c5 = Override.class;        // 注解
    Class<ElementType> c6 = ElementType.class;  // 枚举
    Class<Integer> c7 = Integer.class;          // 基本数据类型
    Class<Integer> c72 = Integer.TYPE;          // 基本数据另一种
    Class<Void> c8 = void.class;                // void
    Class<Class> c9 = Class.class;                // Class
    // system out结果
    class java.lang.Object
    interface java.lang.Comparable
    class [Ljava.lang.String;
    class [[I
    interface java.lang.Override
    class java.lang.annotation.ElementType
    class java.lang.Integer
    int
    void
    class java.lang.Class

    获取Class对象的方式

    复制

    // 已有对象. Object超类提供的
    public final Class getClass();
    对象.getClass();
    // Class类的静态方法
    Class.forName("包名.类名");
    // 已知类名, 最安全可靠, 性能高######################
    类名.class();
    
    // 基本数据类型的包装类, 可以直接用类名.TYPE
    Integer.TYPE;    // int
    // 也可以通过ClassLoader

    Class类的常用方法

    复制

    static ClassforNae(String name); // 传入类名, 返回Class对象
    Object newInstance(); // 调用缺省构造器, 返回返回Class对象的一个实例
    getName(); // 返回此Class对象所表示的实体的名称, (如类接口数组类void)
    Class getSuperClass(); // 返回父类Class对象
    Class[] getinterfaces(); // 获取当前Class对象的接口
    ClassLoadder getClassLoader(); //获取该类的类加载器
    Constructor[] getConstructors(); // 获取构造器对象们, 以数组保存
    Method getMethod(String name, Class... T);    // 返回一个Method对象, 对象形参为paramType
    Field[] getDeclaredFields();    // 返回属性对象们, 以数组保存

    Java内存

    内存的基本结构

      • 存放基本类型变量, 包含数值
      • 存放引用对象, 在堆中的地址值
    • 堆 (所有线程共享)
      • 存放new的对象 (包括数组)
    • 方法区 (特殊的堆, 所有线程共享)
      • 存放class对象
      • 存放static修饰的东西

    类加载的过程

    1. Load

      加载类的过程

      1. 将.class字节码文件内容读取并加载到内存中
      2. static数据转换成方法区的运行时数据
      3. 生成一个代表被加载类的java.lang.Class对象
    2. Link

      链接过程, 将类的二进制数据合并到JVM的运行状态, static变量赋默认初始化值, 常量赋指定值, 具体为:

      1. 验证: 确保所加载的类信息符合JVM规范

      2. 准备: 为static的数据()分配位于方法区的内存空间, 并设置默认初始 (例子中int m = 0)

      3. 解析: JVM常量池中的符号引用 (常量名) 替换为直接引用 (地址)

        [不太懂, 估计是常量赋指定常量值]

    3. Initialize

      执行"类构造器"()方法

      另外有说明:

      • 初始化类需要先初始化该类的父类

      • JVM会保证()方法在多线程环境中被正确加锁和同步

        ()方法:

        • 是由编译器自动收集的类中的所有类变量的赋值动作和静态代码块中的语句合并产生的. [个人理解: 所有static修饰的数据]
        • 是构造类信息的"类构造器" [个人理解: 不是constructor构造器!]

    类加载过程, 自己理解的小结:

    1. Load, 加载 在堆中生成该类对应的Class类对象
    2. Link, 链接, 静态变量分配内存空间, 赋默认初值(int是0, 常量则是具体常量真实值, 云云...)
    3. Initialize, 初始化, 执行static修饰的部分 + 赋程序意图指定的值

    什么时候类会初始化?

    结论: "类的主动引用", 会初始化类; "类的被动引用", 不会初始化类

    • 主动引用

      • JVM启动, 先初始化main()所在类

      • new 对象

        注: 如果有父类, 需要先初始化父类

      • 类名直接调用static成员

        注: 不包括static final, 也就是常量

      • 反射, 得到Class对象

        : 只有Class.forName()是主动引用, 类名.class不是主动引用

    • 被动引用

      • 访问静态field, 声明该field的类才会初始化. (通过子类访问父类的静态属性, 只初始化父类)
      • new以该类为元素类型的数组, 只开辟相应内存空间, 不主动初始化该类
      • 类名直接调用常量. (常量是static final, 这一点与主动引用中类名调用static成员相结合理解)

    代码演示:

    复制

    package com.kuang.reflection;
    // 测试类什么时候会初始化
    public class Test06 {
        static {
            System.out.println("main所在类被加载");
        }
        public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
            // 1. new 类
    //        Son son = new Son();
            // 2. 反射
    //        Class<?> aClass1 = Class.forName("com.kuang.reflection.Son");   // 只是反射, 就已经全部加载
    //        Class<Son> sonClass = Son.class;    // 只加载main
    //        Class<? extends Son> aClass = son.getClass(); // 全部加载
    //        aClass.newInstance();
    //        sonClass.newInstance();
    
    
      // 不会产生类主动引用的操作

// System.out.println(Son.b); // 只加载main和父

// Son[] arr = new Son[5]; // 只加载main, 说明数组只开辟了空间

      System.out.println(Son.M);  // 只加载main, 因: 常量池中

      /*
      测试调用静态成员方法
       */

// System.out.println(Son.m);

  }

}
class Father{
static int b = 2;

  static {
      System.out.println("父类被加载");
  }

}
class Son extends Father{
static {
System.out.println("子类被加载");
m = 300;
}
static int m = 100;
static final int M = 1;
}

  ## ClassLoader

  **类加载器的作用**

  1. 将*.class源文件内容加载到内存中, 将这些静态数据转换成方法区的运行时数据
  2. 在堆中生成一个代表这个类的java.lang.Class对象, 作为方法区中类数据的访问入口

  **"类缓存"**

  类加载到类加载器中, 将维持加载(缓存)一段时间, Class对象会存在一段时间, 就是类缓存. JVM的GC会回收这些Class对象.

  **JVM规范定义了如下类型的类加载器:**

  - **Bootstap Classloader:** 引导类加载器, C++编写, 负责Java平台核心库rt.jar, 该类加载器无法直接获取注

    > 无法直接获取, 表现为执行`.getParent()`返回`null`

  - **Extension Classloader:** 扩展类加载器, 负责jre/lib/ext/*.jar

  - **SystemApplication Classloader:** 系统类加载, java -classpath下的.jar, 最常用的加载器

  - 自定义类加载器

  **补充: rt.jar是什么? java.lang的根目录在哪里?**

  复制

  ```java
  // 位置:
  位于jdk文件, ...jdk1.8.0_71jrelibrt.jar
  // 用解压缩软件打开rt.jar:
  com, java, javax, jdk, META-INF, org, sun
  // 打开java:
  applet, awt, io, lang, math, rio, rmi, security, sql, text, time, util

*代码演示: 获取各类加载器 *

复制

  /*获取类加载器*/
  public class Test07 {
      public static void main(String[] args) {
          // 获取SystemClassLoader
          ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
          System.out.println(systemClassLoader);  // sun.misc.Launcher$AppClassLoader@14dad5dc
          // 获取SystemClassLoader的父类加载器: ExtClassLoader
          ClassLoader parent = systemClassLoader.getParent();
          System.out.println(parent);     // sun.misc.Launcher$ExtClassLoader@1b6d3586
          // 获取扩展类加载器的父类加载器: 根加载器 (c++)
          ClassLoader parent1 = parent.getParent();
          System.out.println(parent1);    // null, 可能是java语言层面无法理解, 无法直接获取


          // 获取当前Test07类是哪个类加载器负责的
          Class<?> c1 = Class.forName("com.kuang.reflection.Test07");
          ClassLoader classLoader = c1.getClassLoader();
          System.out.println(classLoader);    // sun.misc.Launcher$AppClassLoader@14dad5dc

          // 获取Object类是由哪个类加载器负责加载的
          Class<?> c2 = Class.forName("java.lang.Object");
          ClassLoader classLoader2 = c2.getClassLoader();
          System.out.println(classLoader2);   // null

      }
  }

双亲委派机制

此机制可以确保, 假如手写的包如果和核心类库包路径名称雷同时, 不会错误地加载自己手写的包, 从而保证了Java核心类库的安全

反射获取运行时类的结构信息

  package com.kuang.reflection;
  import java.lang.reflect.Constructor;
  import java.lang.reflect.Field;
  import java.lang.reflect.Method;
  /*获得类的信息*/
  public class Test08 {
      public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
          Class<?> c1 = Class.forName("com.kuang.reflection.User");

          // 获得类的名字
          System.out.println(c1.getName());       // 获得完整类名
          System.out.println(c1.getSimpleName()); // 获得单纯类名

          // 获得类的所有属性
          Field[] fields = c1.getFields();    // 只能找到public属性
          for (Field field : fields) {
              System.out.println(field);  // 结果猜错了, 无结果
          }

          fields = c1.getDeclaredFields();    // 这个才能获取非public的, 更完整的属性们
          for (Field field : fields) {
              System.out.println(field);
          }
          /*
            private java.lang.String com.kuang.reflection.User.name
            private int com.kuang.reflection.User.id
            private int com.kuang.reflection.User.age
           */
          System.out.println("######################################");

          // 获得指定field值
  //        Field name = c1.getField("name");   // 报错, 找不到这个field, 因为方法找不到private的
  //        System.out.println(name);
          Field name2 = c1.getDeclaredField("name");
          System.out.println(name2);

          // 获得method
          System.out.println("方法: ");
          Method[] methods = c1.getMethods(); // 获得本类及父类的全部public方法
          for (Method method : methods) {
              System.out.println(method);
          }
          System.out.println("Declared方法");   // 获得本类的所有方法
          methods = c1.getDeclaredMethods();
          for (Method method : methods) {
              System.out.println(method);
          }

          // 获得指定方法
          System.out.println(c1.getMethod("getName", null));
          System.out.println(c1.getMethod("setName", String.class));
          System.out.println("######################################");

          // 获得所有构造器
          Constructor<?>[] constructors = c1.getConstructors();
          for (Constructor<?> constructor : constructors) {
              System.out.println(constructor);
          }
          constructors = c1.getDeclaredConstructors();
          for (Constructor<?> constructor : constructors) {
              System.out.println(constructor);
          }
          System.out.println("######################################");
          Constructor<?> declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
          System.out.println(declaredConstructor);
      }
  }

动态创建对象

通过反射创建对象 (一般情况)

User (目标类) 的Class对象调用.newInstance(), 要求:

  1. 必须确保该类有无参构造器

  2. 必须确保该构造器的访问权限合适

    复制

    // 获得Class对象
    Class c1 = Class.forName("com.kuang.reflection.User");
    // 构造一个对象
    User user = (User)c1.newInstance();  // 本质是调用了类的无参构造

    通过"构造器对象"创建对象 (不存在无参构造)

    假如不存在无参构造器, 就不能通过上一条途径, 但还可以通过Constructor对象, 传入指定参数构建对象, 步骤如下:

  3. 获得指定某重载构造器对象, .getDeclaredConstructor(Class... parameterTypes), 参数为目标对象参数类型的Class对象String.class, int.class...

  4. 构造器对象.newInstance(目标对象需要具体参数)

    复制

    Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
    User user2 = (User) constructor.newInstance("zhangsan", 1001, 18);

    通过反射调用成员:

    method

    借助Method类完成, 步骤如下:

  5. c1.getDeclaredMethod()getMethod(), 参数: 方法名, 参数类型.class...

  6. method对象.invoke(对象, 具体参数们...)

    复制

    // 通过反射调用成员方法
    User user3 = (User) c1.newInstance();
    // 通过反射获取方法
    Method setName = c1.getDeclaredMethod("setName", String.class);
    setName.invoke(user3, "狂神");    // 调用

    field

    复制

    // 通过反射操作属性
    User user4 = (User) c1.newInstance();
    Field name = c1.ge tDeclaredField("name");
    name.setAccessible(true);   // 操作私有属性之前需要操作
    name.set(user4, "李四");
    System.out.println(user4);  // 报错, 属性是private的, 不能直接操作,需要setAccessible

注意: 如果目标方法构造器或属性为private, 则需要提前.setAccessible(true);

setAccessible(),是"访问安全检查的开关"

  • true, 取消访问安全检查, 代码效率也会更高
  • false, 实施访问安全检查

测试: 性能分析

复制

  package com.kuang.reflection;

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

  /*分析性能问题*/
  public class Test10 {
      // 普通方式调用
      public static void test01(){
          User user = new User();
          long startTime = System.currentTimeMillis();
          for (int i = 0; i < 1000000000; i++) {
              user.getName();
          }
          long endTime = System.currentTimeMillis();
          System.out.println("普通方式执行10亿次:" + (endTime - startTime) + "ms");
      }
      // 反射方式调用
      public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
          User user = new User();
          Class<? extends User> c1 = user.getClass();
          Method getName = c1.getDeclaredMethod("getName", null);
          long startTime = System.currentTimeMillis();
          for (int i = 0; i < 1000000000; i++) {
              getName.invoke(user, null);
          }
          long endTime = System.currentTimeMillis();
          System.out.println("反射1方式执行10亿次:" + (endTime - startTime) + "ms");
      }
      // 关闭访问安全检测
      public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
          User user = new User();
          Class<? extends User> c1 = user.getClass();
          Method getName = c1.getDeclaredMethod("getName", null);
          getName.setAccessible(true);
          long startTime = System.currentTimeMillis();
          for (int i = 0; i < 1000000000; i++) {
              getName.invoke(user, null);
          }
          long endTime = System.currentTimeMillis();
          System.out.println("反射2方式执行10亿次:" + (endTime - startTime) + "ms");
      }
      public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
          test01();
          test02();
          test03();
      }

  }

结果:

复制

  普通方式执行10亿次:4ms
  反射1方式执行10亿次:2134ms
  反射2方式执行10亿次:1266ms

  普通方式执行10亿次:5ms
  反射1方式执行10亿次:2183ms
  反射2方式执行10亿次:1246ms

  普通方式执行10亿次:5ms
  反射1方式执行10亿次:2087ms
  反射2方式执行10亿次:1252ms   

反射获取泛型数据

代码演示:

复制

  package com.kuang.reflection;

  import java.lang.reflect.Method;
  import java.lang.reflect.ParameterizedType;
  import java.lang.reflect.Type;
  import java.util.List;
  import java.util.Map;

  /*通过反射获取泛型*/
  public class Test11 {
      public void test01 (Map<String, User> map, List<User> list, String string, User user) {
          System.out.println("test01");
      }
      public Map<String, User> test02(){
          System.out.println("test02");
          return null;
      }
      public static void main(String[] args) throws NoSuchMethodException {
          /*1. 获取方法参数列表中的泛型数据*/
          System.out.println("test01()");
          Method methodTest01 = Test11.class.getMethod("test01", Map.class, List.class, String.class, User.class);
          Type[] genericParameterTypes = methodTest01.getGenericParameterTypes();
          for (Type genericParameterType : genericParameterTypes) {   // 遍历方法的参数, 还需要取具体泛型
              System.out.println(genericParameterType);
              if (genericParameterType instanceof ParameterizedType) {    // 如果是泛型
                  Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();// 就获取真实类型(ActualType)
                  for (Type actualTypeArgument : actualTypeArguments) {
                      System.out.println("方法参数列表中的泛型: " + actualTypeArgument);
                  }
              }
          }
          /*2. 获取方法返回值中的泛型数据*/
          System.out.println("test02()");
          Method test02Method = Test11.class.getMethod("test02");
          Class<?> returnType = test02Method.getReturnType(); // 这种方法不能获取其中的泛型部分
          Type genericReturnType = test02Method.getGenericReturnType();
          System.out.println(returnType);
          System.out.println(genericReturnType);
          if (genericReturnType instanceof ParameterizedType) {
              Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
              for (Type actualTypeArgument : actualTypeArguments) {
                  System.out.println("方法返回值中的泛型类型:" + actualTypeArgument);
              }
          }
      }
  }
  D:Softwarejdk1.8.0_71binjava.exe "-javaagent:D:Program FilesJetBrainsIntelliJ IDEA 2019.2libidea_rt.jar=64909:D:Program FilesJetBrainsIntelliJ IDEA 2019.2bin" -Dfile.encoding=UTF-8 -classpath D:Softwarejdk1.8.0_71jrelibcharsets.jar;D:Softwarejdk1.8.0_71jrelibdeploy.jar;D:Softwarejdk1.8.0_71jrelibextaccess-bridge-64.jar;D:Softwarejdk1.8.0_71jrelibextcldrdata.jar;D:Softwarejdk1.8.0_71jrelibextdnsns.jar;D:Softwarejdk1.8.0_71jrelibextjaccess.jar;D:Softwarejdk1.8.0_71jrelibextjfxrt.jar;D:Softwarejdk1.8.0_71jrelibextlocaledata.jar;D:Softwarejdk1.8.0_71jrelibextnashorn.jar;D:Softwarejdk1.8.0_71jrelibextsunec.jar;D:Softwarejdk1.8.0_71jrelibextsunjce_provider.jar;D:Softwarejdk1.8.0_71jrelibextsunmscapi.jar;D:Softwarejdk1.8.0_71jrelibextsunpkcs11.jar;D:Softwarejdk1.8.0_71jrelibextzipfs.jar;D:Softwarejdk1.8.0_71jrelibjavaws.jar;D:Softwarejdk1.8.0_71jrelibjce.jar;D:Softwarejdk1.8.0_71jrelibjfr.jar;D:Softwarejdk1.8.0_71jrelibjfxswt.jar;D:Softwarejdk1.8.0_71jrelibjsse.jar;D:Softwarejdk1.8.0_71jrelibmanagement-agent.jar;D:Softwarejdk1.8.0_71jrelibplugin.jar;D:Softwarejdk1.8.0_71jrelibresources.jar;D:Softwarejdk1.8.0_71jrelibrt.jar;D:Software-ideaProject-QinJiangJavaSEoutproductionreflectandnotation com.kuang.reflection.Test11
  test01()
  java.util.Map<java.lang.String, com.kuang.reflection.User>
  方法参数列表中的泛型: class java.lang.String
  方法参数列表中的泛型: class com.kuang.reflection.User
  java.util.List<com.kuang.reflection.User>
  方法参数列表中的泛型: class com.kuang.reflection.User
  class java.lang.String
  class com.kuang.reflection.User
  test02()
  interface java.util.Map
  java.util.Map<java.lang.String, com.kuang.reflection.User>
  方法返回值中的泛型类型:class java.lang.String
  方法返回值中的泛型类型:class com.kuang.reflection.User

涉及到的独特而重要的方法:

  • methodTest01.getGenericParameterTypes(), 返回方法对象中所有参数的Type[], 包括泛型和泛型参数的

  • genericParameterType instanceof ParameterizedType, 判断这个Type对象是否属于"泛型Type(参数化类型)"

  • ((ParameterizedType) genericParameterType).getActualTypeArguments(), 抽取Map<String, User>中的String, User的Type对象. 注意需要强转.

    反射获取注解数据

    代码演示:

    复制

    package com.kuang.reflection;
    
    import java.lang.annotation.*;
    import java.lang.reflect.Field;
    
    /*反射获取注解*/
    public class Test12 {
      public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
          /*获取类的Student2类的注解的Class对象*/
          Class<?> c1 = Class.forName("com.kuang.reflection.Student2");
          Annotation[] annotations = c1.getAnnotations();
          for (Annotation annotation : annotations) {
              System.out.println(annotation);
          }
          /*获取注解对象的成员: value*/
          Tablekuang tablekuang = c1.getAnnotation(Tablekuang.class);
          String value = tablekuang.value();
          System.out.println(value);
    
          /*获取类成员的注解*/
          Field field = c1.getDeclaredField("name");
          Annotation[] annotations1 = field.getAnnotations();
          for (Annotation annotation : annotations1) {
              System.out.println(annotation);
          }
          Fieldkuang annotation = field.getAnnotation(Fieldkuang.class);
          System.out.println(annotation.columnName());
          System.out.println(annotation.type());
          System.out.println(annotation.length());
      }
    }
    
    /*声明类名的注解*/
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE})
    @interface Tablekuang{
      String value();
    }
    /*声明属性的注解*/
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.FIELD})
    @interface Fieldkuang{
    
      String columnName();
      String type();
      int length();
    }
    
    

/POJO/
@Tablekuang("db_student")
class Student2{

  @Fieldkuang(columnName="db_name", type="varchar", length = 10)
  private String name;
  @Fieldkuang(columnName="db_id", type="int", length = 10)
  private int id;
  @Fieldkuang(columnName="db_age", type="int", length = 10)
  private int age;

  public Student2() {
  }

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

  public String getName() {
      return name;
  }

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

  public int getId() {
      return id;
  }

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

  public int getAge() {
      return age;
  }

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

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

}