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里必须要有无参构造方法, 否则会报异常 
  1. 动态加载类
  • 编译时加载类是静态加载类
    new 创建对象是静态加载类, 在编译时刻就需要加载所有可能使用到的类, 如果有一个缺失, 那么整个文件都无法通过编译;
  • 运行时加载类是动态加载类
    Class c = Class.forName(“类全名”), 不仅表示了类的类型, 还表示了动态加载类, 编译不会报错, 在运行时才会加载, 使用接口标准能更方便胴体加载类的实现. 功能性的类尽量使用动态加载, 而不用静态加载.
  1. 获取方法信息
  • 基本的数据类型, 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("---------------------------------");
        }
}
  1. 获取成员变量构造函数信息
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());
            }
        }
}
  1. 方法反射的基本操作
  • 如何获取某个方法
    方法的名称和方法的参数列表才能决定某个方法
    Method m = c.getDeclaredMethod("方法名", 可变参数列表(参数类型.class))
  • 方法的反射操作
    m.invoke(对象, 参数列表)
    方法如果没有返回值, 返回null, 如果有返回值, 返回Object,
    然后再强转为原函数的返回值类型;
  1. 通过反射了解集合泛型的本质
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,

上一篇:org/aspectj/weaver/reflect/ReflectionWorld ReflectionWorldEx


下一篇:Go36-44,45-文件操作(os.File)