美文网首页
Java进阶-反射机制

Java进阶-反射机制

作者: 虐心笔记 | 来源:发表于2020-12-06 11:27 被阅读0次

    一、概念

    Java反射是Java被视为动态(或准动态)语言的一个关键性质。指在程序运行状态中,可以构造任意一个类,可以调用获取任意一个类的方法、变量、构造器等。这种动态获取信息以及动态调用对象方法的功能称为 java 语言的反射机制。反射被视为动态语言的关键,框架设计的灵魂。《百度百科》

    以上的解释不理解没关系,下面来结合图分析程序执行的流程:
    首先javac编译源代码之后,通过类加载器进内存生成字节码文件对象。一个类的字节码文件大概可以拆分为:包信息、成员变量、构造方法、成员方法等等,个个组成部分映射成一个个对象。图中就是类的正常加载过程,反射的原理就在于如何去操作这些对象

    Java代码在计算机中大致分为三个阶段:Source、Class、Runtime

    image.png

    Person person = new Person(); 执行!
    JVM 启动后,javac 会把 Person.java 编译成一个 Person.class 文件,通过 ClassLoader 加载进内存的方法区中,类对象是用来描述字节码文件对象的,每个类只有一个class对象且只会加载一次,作为方法区类的数据结构的接口。然后创建Person的类实例到堆(Heap)内存中。注意:Jvm 在创建对象前,会先检查类是否加载,寻找类对应的class对象,加载完成之后,会给对象分配内存。

    了解大致过程之后,发现Person是new出来写死给Jvm执行的,假设 当我们的程序在运行时,需要动态的加载一些类,这些类可能运行之前不确定是否需要加载到Jvm中,而是在运行时根据需要才去加载 。这就需要用到反射机制。

    举个栗子:目前主流开发所用的 spring 框架,在配置各种各样的 bean 时,是以配置文件的形式配置的,spring 容器会根据配置的信息去动态加载类。 Spring的工作原理就是让一个对象的创建不用new就可以自动的生产,在运行时与xml Spring的配置文件来动态的创建对象和调用对象,而不需要通过代码来关联。

    相关类
    类名 释义
    Class 类对象,在运行时java类数据结构的接口
    Field 类属性(成员变量)
    Method 类方法
    Constructor 类构造方法

    二、Class类

    Class对象的三种获取方式

    public class Reflection {
     /**
     * 1.Source阶段,通过将class字节码文件加载进内存,返回 class 对象
     * 2.class对象阶段,通过类名的属性 class 来获取
     * 3.Runtime阶段,通过Object.getClass()方法来获取
     *
     * @param args anything
     * @throws ClassNotFoundException
     */
     public static void main(String[] args) throws ClassNotFoundException {
     // 1.多用于配置文件,将类名配置在文件中,读取文件加载类
     Class aClass = Class.forName("com.angst.student.Reflection");
     // 2\. 一般用于参数传递,当做参数使用
     Class aClass1 = Reflection.class;
     // 3\. 多用于对象获取字节码
     Class aClass2 = new Reflection().getClass();
    
     }
    }
    
    Class
    方法 释义
    getClassLoader() 获得类的加载器
    getClasses() 返回一个数组,数组中包含该类中所有公共类和接口类的对象
    getDeclaredClasses() 返回一个数组,数组中包含该类中所有类和接口类的对象
    getName() 获得类的完整路径名字
    getPackage() 获得类的包
    getSimpleName() 获得类的名字
    getSuperclass() 获得当前类继承的父类的名字
    getInterfaces() 获得当前类实现的类或是接口
    asSubclass() 把传递的类的对象转换成代表其子类的对象
    newInstance() 创建一个空参的对象(无参构造使用)
    /**
     *  通过类完整路径获取 Class 对象
     * @param packagePath 包路径
     * @return  class
     * @throws ClassNotFoundException
     */
     public static Class<?> getClass(String packagePath) throws ClassNotFoundException {
     return Class.forName(packagePath);
     }
    
     /**
     *  通过包名+类名返回 class 对象
     * @param packagePath 包路径
     * @param obj  类名
     * @return class
     * @throws ClassNotFoundException
     */
     public static Class<?> getClass(String packagePath, String obj) throws ClassNotFoundException {
     if (obj.indexOf(".") == 0) { obj = packagePath + obj; }
     return getClass(obj);
     }
    
    Constructor
    方法 释义
    getConstructor() 获得该类中与参数类型匹配的public构造方法
    getConstructors() 获得该类的所有public构造方法
    getDeclaredConstructor() 获得该类中与参数类型匹配的构造方法
    getDeclaredConstructors() 获得该类所有构造方法
    newInstance() 根据传递的参数创建类的对象(new Object)
    /**
     * 获取构造器
     * @param clazz  类对象
     * @param parameterTypes  参数类型
     * @return Constructor
     * @throws NoSuchMethodException
     */
     public static Constructor getConstructor(Class clazz, Class... parameterTypes) throws NoSuchMethodException {
     return clazz.getDeclaredConstructor(parameterTypes);
     }
    
    Field
    方法 释义
    getField() 获得某个public属性对象
    getFields() 获得所有public属性对象
    getDeclaredField() 获得某个属性对象(包括私有)
    getDeclaredFields() 获得所有属性对象(包括私有)
    setAccessible() 设置为true,忽略访问权限修复符,操作 private 类属性(暴力反射不安全)
    /**
     * 设置某个字段的值
     * @param clazz 类对象
     * @param fieldName  属性字段名称
     * @param value 设置值
     * @throws Exception
     */
     public static void setField(Class<Object> clazz, String fieldName, String value) throws Exception {
     Object obj = clazz.newInstance();
     Field declaredField = clazz.getDeclaredField(fieldName);
     declaredField.setAccessible(true);
     declaredField.set(obj, value);
     }
    
     /**
     *   获取某个字段的值
     * @param clazz
     * @param fieldName
     * @return
     * @throws Exception
     */
     public static Object getField(Class<Object> clazz, String fieldName) throws Exception {
     Object obj = clazz.newInstance();
     Field declaredField = clazz.getDeclaredField(fieldName);
     declaredField.setAccessible(true);
     return declaredField.get(obj);
     }
    
     /**
     *  获取所有字段名称列表
     * @param clazz
     * @return
     */
     public static List<Object> getFields(Class<Object> clazz) throws IllegalAccessException, InstantiationException {
     List<Object> list = new ArrayList<>();
     Object obj = clazz.newInstance();
     Field[] fieldList = clazz.getDeclaredFields();
     for (Field fld : fieldList) {
     list.add(fld.get(obj));
     }
     return list;
     }
    
     /**
     * 获取所有字段名称+类型以 hashMap 返回
     * @param clazz
     * @return
     */
     public static HashMap<String, Class<?>> getFieldMap(Class clazz) {
     HashMap<String, Class<?>> hashMap = new HashMap<String, Class<?>>();
     Field[] fieldList = clazz.getDeclaredFields();
     for (Field fld : fieldList) {
     hashMap.put(fld.getName(), fld.getType());
     }
     return hashMap;
     }
    
    
    Method
    方法 释义
    getMethod() 获得该类某个 Public 方法
    getMethods() 获得该类所有 Public 方法
    getDeclaredMethod() 获得该类某个方法(包括私有)
    getDeclaredMethods() 获得该类所有方法(包括私有)
    invoke() 根据传递的object对象及参数调用该对象的方法
     * 获取所有方法列表
     * @param clazz 类对象
     * @return
     */
     public static List<Object> getMethods(Class clazz) {
     List<Object> list = new ArrayList<>();
     for (Method declaredMethod : clazz.getDeclaredMethods()) {
     declaredMethod.setAccessible(true);
     list.add(declaredMethod);
     }
     return list;
     }
    
     /**
     * 指定方法方法调用
     * @param clazz 类对象
     * @param methodName 调用的方法名称
     * @param value 方法入参
     * @param type 入参类型
     * @throws Exception
     */
     public static void invokeMethod(Class clazz, String methodName, String value, Class... type) throws Exception {
     Object obj = clazz.newInstance();
     Method method = clazz.getDeclaredMethod(methodName, type);
     method.invoke(obj, value);
     }
    
    Other
    方法 释义
    isAnnotation() 如果是注解类型则返回true
    isAnnotationPresent() 如果是指定类型注解类型则返回true
    isAnonymousClass() 如果是匿名类则返回true
    isArray() 如果是一个数组类则返回true
    isEnum() 如果是枚举类则返回true
    isInstance() 如果obj是该类的实例则返回true
    isInterface() 如果是接口类则返回true
    isLocalClass() 如果是局部类则返回true
    isMemberClass() 如果是内部类则返回true

    案例

    从上面的一些方法示例来看,反射的操作过程比较麻烦,还不如使用new对象来的简单直接。更别说反射是框架设计的灵魂这一说法,并没有体现出本身的任何价值。下面会通过简单的案例演示,可以初步了解到反射的魅力所在。

    需求:在不改变该类任何代码前提下,任意创建类对象,并且执行其中任意方法。通过配置文件来实现反射机制。

    步骤:
    1.将需要创建的对象的类路径和需要执行的方法定义在配置文件中
    2.在程序中加载读取配置文件,并通过反射机制将类加载进内存
    3.创建对象,执行指定方法

    配置文件定义
    #pro.properties
    className=org.example.basic.Person
    methodName=eat
    
    配置类
    package org.example.basic;
    
    public class Person {
    
     public void eat() {
     System.out.println("anything...");
     }
    }
    
    反射类
    package org.example.basic;
    
    import java.io.IOException;
    import java.lang.reflect.Method;
    import java.util.Properties;
    
    
    public class Reflection {
     private static String className;
     private static String methodName;
    
     /**
     * 加载配置文件
     */
     static {
     Properties properties = new Properties();
     try {
     properties.load(Reflection.class.getClassLoader().getResourceAsStream("pro.properties"));
     className = properties.getProperty("className");
     methodName = properties.getProperty("methodName");
     } catch (IOException e) {
     e.printStackTrace();
     }
     }
    
     /**
     * 加载类进内存,创建对象,执行指定方法
     * @throws Exception
     */
     public static void invokeMethod() throws Exception {
     Class<?> aClass = Class.forName(className);  // 类加载进内存,返回类对象
     Object obj = aClass.newInstance();  // 创建对象
     Method method = aClass.getMethod(methodName);  // 获取指定方法
     method.invoke(obj);  // 执行方法调用
     }
    
    
     public static void main(String[] args) throws Exception {
     Reflection.invokeMethod();  // 方法调用输出:anything...
    
     }
    }
    

    Epilogue

    以上就是通过动态加载配置文件的类和方法,在不改变该类任何代码前提下,可以实现任意创建类对象,并且执行其中任意方法。可以达到一个解耦的目的,提升程序的扩展性。

    反思?

    通过以上的反射相关类及方法的介绍和案例的使用,已经初步了解的Java反射机制的强大之处,那么如何结合反射应用到实际工作中或者编写一个简单“测试框架”?

    相关文章

      网友评论

          本文标题:Java进阶-反射机制

          本文链接:https://www.haomeiwen.com/subject/delowktx.html