zoukankan      html  css  js  c++  java
  • Java reflect 反射学习笔记

    1. class 类的使用

    • 万事万物皆对象 (基本数据类型, 静态成员不是面向对象), 所以我们创建的每一个类都是对象, 即类本身是java.lang.Class类的实例对象, 但是这些对象不需要 new 出来, 因为java.lang.Class类的构造方法是私有的;
    • 任何一个类都是Class类的实例对象.这个实例对象有_三种表达方式_: (我们新建一个Student类)
    Class c1 = Student.class; // 实际告诉我们任何一个类都有一个隐含的静态成员变量class(知道类名时用)
    Student stu = new Student();
    Class c2 = stu.getClass(); // 已知该类的对象通过getClass方法(知道对象时用)
    try {
       Class c3 = Class.forName("com.cnblogs.reflect.Student");// 会有一个ClassNotFoundException异常
    } catch (ClassNotFoundException e) {
       e.printStackTrace();
    }
    

    ps: 官网解释说, c1, c2表示 Student类的类类型(class type), 类也是对象, 是Class类的实例对象, 这个对象我们称之为该类的_类类型_

    这里有一点值得注意的是: 当我们执行System.out.println(c1 == c2) 时, 结果是true, 这是为什么? 原因是不管c1, c2都代表了Student类的类类型, 一个类可能是Class类的一个实例对象.

    我们完全可以通过类的类类型创建该类的对象实例, 即通过c1或者c2创建Student的实例;

    Student stu = (Student) c1.newInstance(); // 前提是Student里必须要有无参构造方法, 否则会报异常
    

    2. 动态加载类

    • 编译时加载类是静态加载类
      new 创建对象是静态加载类, 在编译时刻就需要加载所有可能使用到的类, 如果有一个缺失, 那么整个文件都无法通过编译;
    • 运行时加载类是动态加载类

    3. 获取方法信息

    • 基本的数据类型, void关键字都存在类类型
    Class c1 = int.class; // int的类类型
    Class c2 = String.class; // String的类类型, 可以理解为编译生成生成的那个String.class字节码文件
    Class c3 = double.class;
    Class c4 = Double.class;
    Class c5 = void.class;
    
    • Class类的基本API操作
    public static void printClassMessage(Object obj) {
            Class c = obj.getClass();
            // 获取类的类名称;
            System.out.println("类的名称是: " + c.getName());
            System.out.println("---------------------------------");
    
            Method[] methods = c.getMethods();
            for (Method method : methods) {
                // 得到方法返回值类型的类类型
                Class returnType = method.getReturnType();
                System.out.println("returnType = " + returnType.getName());
                // 得到方法的名称
                System.out.println("method = " + method.getName());
                // 获取参数类型 -> 得到的是参数列表的类类型
                Class<?>[] parameterTypes = method.getParameterTypes();
                for (Class parameterClass : parameterTypes) {
                    System.out.println("parameterClass = " + parameterClass.getName());
                }
                System.out.println("---------------------------------");
            }
    }
    

    4. 获取成员变量构造函数信息

    public static void printFieldMessgae(Object obj) {
    
            Class<?> c = obj.getClass();
            Field[] fields = c.getDeclaredFields();
            for (Field field : fields) {
                // 得到成员变量的类型的类类型
                Class<?> fieldType = field.getType();
                String typeName = fieldType.getName();
                System.out.println("typeName = " + typeName);
                // 得到成员变量的名称
                String fieldName = field.getName();
                System.out.println("fieldName = " + fieldName);
            }
            /**
             * 获取构造函数
             */
            Constructor<?>[] constructors = c.getDeclaredConstructors();
            for (Constructor<?> constructor : constructors) {
                System.out.println("constructor = " + constructor.getName());
                /**
                 * 获取构造函数的参数的类类型
                 */
                Class<?>[] parameterTypes = constructor.getParameterTypes();
                for (Class<?> parameterType : parameterTypes) {
                    System.out.println("parameterType = " + parameterType.getName());
                }
            }
    }
    

    5. 方法反射的基本操作

    • 如何获取某个方法
      方法的名称和方法的参数列表才能决定某个方法
      Method m = c.getDeclaredMethod("方法名", 可变参数列表(参数类型.class))
    • 方法的反射操作
      m.invoke(对象, 参数列表)
      方法如果没有返回值, 返回null, 如果有返回值, 返回Object,
      然后再强转为原函数的返回值类型;

    6. 通过反射了解集合泛型的本质

    ArrayList list1 = new ArrayList();
    ArrayList<String> list2 = new ArrayList();
    
    Class c1 = list1.getClass();
    Class c2 = list2.getClass();
    
    System.out.println(c1 == c2); // -> true
    

    Ps: 因为反射的操作都是编译之后的操作, 也就是运行时的操作, c1 == c2返回true, 说明编译之后集合的泛型是去泛型化的.

    那么我们可以理解为, Java集合中的泛型, 是用于泛指错误类型元素输入的, 比如在list2中我们add一个int, add(10)就会编译保错, 那么这个泛型就可以只在编译阶段有效, 通过了编译阶段, 泛型就不存在了. 可以验证, 我们绕过编译, 用反射动态的在list2中add一个int是可以成功的. 只是这时因为list2中存储了多个不同类型的数据(String和int), 就不能用for-each来遍历了, 会抛出类型转换错误.

  • 相关阅读:
    最近公共祖先
    洛谷 P1076 寻宝 解题报告
    洛谷 P1121 环状最大两段子段和 解题报告
    洛谷 P3705 [SDOI2017]新生舞会 解题报告
    洛谷 P1381 单词背诵 解题报告
    洛谷 P3155 [CQOI2009]叶子的染色 解题报告
    洛谷 P3237 [HNOI2014]米特运输 解题报告
    洛谷 P2345 奶牛集会 解题报告
    洛谷 P1582 倒水 解题报告
    数论知识荟萃
  • 原文地址:https://www.cnblogs.com/liuyishi/p/10353676.html
Copyright © 2011-2022 走看看