• Java反射


    一、什么是反射?

    二、反射与正射

    三、使用


    一、什么是反射?

    1.概念:

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
    要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法。所以先要获取到每一个字节码文件对应的Class类型的对象.。反射就是把Java类中的各种成分映射成一个个的Java对象。

    例如:
    一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把各个组成部分映射成一个个对象。(其实:一个类中这些成员方法、构造方法,在加入类中都有一个类来描述)
    如图是类的正常加载过程:反射的原理在于class对象。

    熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。

    类的正常加载过程:

    2.好处:

    • 可以在程序运行过程中,操作这些对象。
    •  可以解耦,提高程序的可扩展性。

    二、反射与正射

    反射之中包含了一个「反」字,所以想要解释反射就必须先从「正」开始解释。

    一般情况下,我们使用某个类时必定知道它是什么类,是用来做什么的。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。
    如:

    1 Phone phone = new Phone(); //直接初始化,「正射」
    2 phone.setPrice(4);

    上面这样子进行类对象的初始化,我们可以理解为「正」。

    而反射则是一开始并不知道我要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。

    这时候,我们使用 JDK 提供的反射 API 进行反射调用:

    1 Class clz = Class.forName("com.xxp.reflect.Phone");
    2 Method method = clz.getMethod("setPrice", int.class);
    3 Constructor constructor = clz.getConstructor();
    4 Object object = constructor.newInstance();
    5 method.invoke(object, 4);
    上面两段代码的执行结果,其实是完全一样的。但是其思路完全不一样,第一段代码在未运行时就已经确定了要运行的类(Phone),而第二段代码则是在运行时通过字符串值才得知要运行的类(com.xxp.reflect.Phone)。

    所以说什么是反射?反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。

    一个简单的例子:
    上面提到的示例程序,其完整的程序代码如下:

     1 public class Phone {
     2     private int price;
     3     public int getPrice() {
     4         return price;
     5     }
     6     public void setPrice(int price) {
     7         this.price = price;
     8     }
     9 
    10     public static void main(String[] args) throws Exception{
    11         //正常的调用
    12         Phone phone = new Phone();
    13         phone.setPrice(5000);
    14         System.out.println("Phone Price:" + phone.getPrice());
    15         //使用反射调用
    16         Class clz = Class.forName("com.xxp.api.Phone");
    17         Method setPriceMethod = clz.getMethod("setPrice", int.class);
    18         Constructor phoneConstructor = clz.getConstructor();
    19         Object phoneObj = phoneConstructor.newInstance();
    20         setPriceMethod.invoke(phoneObj, 6000);
    21         Method getPriceMethod = clz.getMethod("getPrice");
    22         System.out.println("Phone Price:" + getPriceMethod.invoke(phoneObj));
    23     }
    24 }

    从代码中可以看到我们使用反射调用了 setPrice 方法,并传递了 6000 的值。之后使用反射调用了 getPrice 方法,输出其价格。上面的代码整个的输出结果是:

    1 Phone Price:5000
    2 Phone Price:6000

    从这个简单的例子可以看出,一般情况下我们使用反射获取一个对象的步骤:

     
    1 //获取类的 Class 对象实例
    2 Class clz = Class.forName("com.xxp.api.Phone");
    3 //根据 Class 对象实例获取 Constructor 对象
    4 Constructor phoneConstructor = clz.getConstructor();
    5 //使用 Constructor 对象的 newInstance 方法获取反射类对象
    6 Object phoneObj = phoneConstructor.newInstance();

    而如果要调用某一个方法,则需要经过下面的步骤:

     
    1 //获取方法的 Method 对象
    2 Method setPriceMethod = clz.getMethod("setPrice", int.class);
    3 //利用 invoke 方法调用方法
    4 setPriceMethod.invoke(phoneObj, 6000);

    到这里,我们已经能够掌握反射的基本使用。但如果要进一步掌握反射,还需要对反射的常用 API 有更深入的理解。

     三、使用

     1 * 获取Class对象的方式:
     2     1. Class.forName("全类名"):将字节码文件加载进内存,返回Class对象
     3         * 多用于配置文件,将类名定义在配置文件中。读取文件,加载类
     4     2. 类名.class:通过类名的属性class获取
     5         * 多用于参数的传递
     6     3. 对象.getClass():getClass()方法在Object类中定义着。
     7         * 多用于对象的获取字节码的方式
     8 
     9     * 结论:
    10         同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
     1 * Class对象功能:
     2 * 获取功能
     3 1. 获取成员变量们
     4 2. 获取构造方法们
     5 3. 获取成员方法们
     6 4. 获取全类名
     7 
     8     * 获取功能:
     9         1. 获取成员变量们
    10             * Field[] getFields() :获取所有public修饰的成员变量
    11             * Field getField(String name)   获取指定名称的 public修饰的成员变量
    12 
    13             * Field[] getDeclaredFields()  获取所有的成员变量,不考虑修饰符
    14             * Field getDeclaredField(String name)  
    15         2. 获取构造方法们
    16             * Constructor<?>[] getConstructors()  
    17             * Constructor<T> getConstructor(类<?>... parameterTypes)  
    18 
    19             * Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)  
    20             * Constructor<?>[] getDeclaredConstructors()  
    21         3. 获取成员方法们:
    22             * Method[] getMethods()  
    23             * Method getMethod(String name, 类<?>... parameterTypes)  
    24 
    25             * Method[] getDeclaredMethods()  
    26             * Method getDeclaredMethod(String name, 类<?>... parameterTypes)  
    27         4. 获取全类名    
    28             * String getName()  
    * Field:成员变量
        * 操作:
            1. 设置值
                * void set(Object obj, Object value)  
            2. 获取值
                * get(Object obj) 
    
            3. 忽略访问权限修饰符的安全检查
                * setAccessible(true):暴力反射
    * Constructor:构造方法
        * 创建对象:
            * T newInstance(Object... initargs)  
    
            * 如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法
    * Method:方法对象
        * 执行方法:
            * Object invoke(Object obj, Object... args)  
    
        * 获取方法名称:
            * String getName:获取方法名
  • 相关阅读:
    MongoDB 副本集搭建 & 副本集扩容
    MongoDB 部署 & 基础命令
    MyBatis常见面试题:#{}和${}的区别是什么?
    MyBatis常见面试题:说说MyBatis的工作原理
    Java四种引用类型回收时机介绍
    Java虚拟机之垃圾回收器
    Servlet中过滤器、监听器和拦截器的区别
    Java里一个线程两次调用start()方法会出现什么情况
    二叉树、二叉查找树、平衡树和红黑树概念及其性质
    转:基于Redis实现延时队列
  • 原文地址:https://www.cnblogs.com/aaaazzzz/p/12801160.html
Copyright © 2020-2023  润新知