反射

Java反射API是Java语言实现动态性的关键,它允许动态的创建对象、赋值、以及调用对象的方法,同时反射也是实现动态代理的关键,涉及到反射相关的几个类主要有 Class、ClassLoader,Field、Method、Constructor、Proxy等。因为在Java中一切皆对象,当然,编译后的class字节码文件也会被JVM创建出一个唯一的Class类型对象,类就是类,对象就是对象,对象有属性可以调方法,任何对象也不例外,无需把这些对象想复杂化。

反射的几个API使用
  • 获取Class对象的几种方式
//一、获取class的几种方式
        Class<?> cls1 = Class.forName("com.example.module01.reflect.Human"); //常用于动态的加载某个class,例如从配置文件获取
        Class<Human> cls2 = Human.class;//常用于调用某个需要class类型参数的方法时的参数传递
        Class<? extends Human> cls3 = new Human().getClass();//常用于在明确类型的情况下获取class
        Class<?> cls4 = RefTest01.class.getClassLoader().loadClass("com.example.module01.reflect.Human");//常用于动态

        System.out.println(cls1 == cls2 ? cls1 == cls3 ? cls1 == cls4 : "不相等" : "不相等"); //true
  • Constructor类API
// 2.1 构造方法相关
        Class<Student> clazz = Student.class;
        Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
        System.out.println("声明的构造方法个数:" + declaredConstructors.length);
        Constructor<?>[] constructors = clazz.getConstructors();//获取所有的构造方法
        for (Constructor<?> constructor : constructors) {
            Class<?> declaringClass = constructor.getDeclaringClass();//获取此构造方法的声明类
            Class<?>[] parameterTypes = constructor.getParameterTypes();//获取不带泛型的构造方法
            Type[] genericParameterTypes = constructor.getGenericParameterTypes();//获取带泛型的构造方法
            Object instance = null;
//            instance = constructor.newInstance();//调用newInstance
            System.out.println("构造方法的声明位置:" + declaringClass + ",构造方法参数个数:" + parameterTypes.length
             + ",带泛型的构造方法参数个数:" + genericParameterTypes.length + ",创建的对象:" + instance);
        }
  • Method API
//2.2 method方法API
        Method[] methods = clazz.getMethods(); //可以获取所有的公开的方法,包括本类的和继承的
        for (Method method : methods) {
            System.out.println("公开的方法:" + method.getName() + "," + method.getDeclaringClass());
        }
        Method[] declaredMethods = clazz.getDeclaredMethods();//获取声明在本类中的所有方法,包括静态方法
        for (Method declaredMethod : declaredMethods) {
            declaredMethod.setAccessible(true);//破解private权限
            declaredMethod.invoke(clazz.newInstance());//反射执行方法
            System.out.println("本类所有方法:" + declaredMethod);
        }

        System.out.println("========================Field=======================");
  • Field API
//2.3 属性相关
        Field[] fields = clazz.getFields();
        for (Field field : fields) {
            System.out.println("公开的属性:" + field.getName());
        }
        for (Field declaredField : clazz.getDeclaredFields()) {
            Student student = clazz.newInstance();
            declaredField.setAccessible(true);//设置可访问
            declaredField.set(student,1);//给属性赋值
            Object value = declaredField.get(student);
            System.out.println("本类的所有属性:" + declaredField.getName()  + "  val ===> " + value);
        }
  • 获取父类 接口API相关信息,通过获取superClass给继承的父类属性赋值
//2.4获取父类、接口信息
        Class<? super Student> superclass = clazz.getSuperclass();
        Class<?>[] interfaces = clazz.getInterfaces();
        System.out.println("父类是:" + superclass); //Object类的superClass是null。
        System.out.println("本类实现的接口:" + interfaces.length);

        //2.5 实现Spring自动注入效果,将父类的protected类型属性也自动注入。
        Class<Student> stuClass = Student.class;
        //通过反射创建对象
        Student instance = stuClass.newInstance();
        //获取本类的方法
        for (Field field : stuClass.getDeclaredFields()) {
            //开启访问权限
            field.setAccessible(true);
            //动态赋值
            field.set(instance,111);
        }
        //继续获取父类的class对象
        Class<? super Student> superClass = stuClass.getSuperclass();
        //继续获取父类的成员属性
        Field[] declaredFields = superClass.getFields();
        for (Field declaredField : declaredFields) {
            //给父类属性赋值
            declaredField.set(instance,222);
        }

        //打印输出
        System.out.println(instance);
  • Class类的几个重要方法
  1. isAssignableFrom 用于判断两个Class对象是否有继承或者实现关系。
  2. Reflection.getCallerClass 获取调用当前方法的Class对象
  3. 在使用反射为静态属性赋值或者调用静态方法时 可以不传target实例对象。
Class类加载过程

用Java反向解析cron表达式反向解析_python

Java类加载主要分为这么几个阶段

  • 编译阶段

将.java源文件编译成.class字节码文件。

  • 运行阶段
  1. 当JVM第一次加载某一个用到的类时,会使用ClassLoader类加载器加.class文件加载到内存中。
  2. 连接-验证阶段:对class进行验证 头文件元数据等。
  3. 连接-准备阶段:收集字节码中的静态属性与静态代码块,并把静态属性赋值默认值。
  4. 连接-解析阶段:解析阶段主要是把符号引用转换为地址应用。
  5. 初始化:初始化阶段主要是运行class的静态代码块内容。

所有阶段都运行完成之后,此时JVM会把二进制的class文件加载到方法区,同时创建一个Class对象放入堆区。这个时候仅仅是完成了类的加载过程,之后才是创建对象。