什么是反射

官方有段介绍是这么说的

Reflection is commonly used by programs which require the ability to examine or modify the runtime behavior of applications running in the Java virtual machine.

什么意思呢,就是说反射通常用于需要能够检查或修改Java虚拟机中运行的应用程序的运行时行为的程序。

不是很好懂,我们说下反射能做什么吧。

  1. 反射可以在运行时动态生成某个类的实例。
  2. 反射可以在运行时获取某个类的任何的变量并修改变量的值。
  3. 反射可以在运行时执行某个类的任何方法。

如何使用反射

我们针对上面的情况一一说一下如何使用。

我们先定义一个 com.reflect.Demo.java 用来做我们后续的实验。

package com.reflect;
public class Demo {

    public String pubStr = "public_string";
    private String priStr = "private_string";

    private String getPriStr(String testParam) {
        pubStr = testParam;
        return priStr;
    }

    private String getPriStr() {
        return priStr;
    }

    public Demo() {

    }

    private Demo( String priStr ) {
        this.priStr = priStr;
    }

    @Override
    public String toString() {
        return "Demo{" +
                "pubStr='" + pubStr + '\'' +
                ", priStr='" + priStr + '\'' +
                '}';
    }
}

在运行时动态生成某个类的实例

// 反射第一步是获取类的 class , 一般有 3 种写法
Class demoClass = Class.forName("com.reflect.Demo");
// demoClass = Demo.class;
// demoClass = new Demo().getClass();

// 获取无参构造函数
Constructor constructor = demoClass.getDeclaredConstructor();
// 设置可以访问
constructor.setAccessible(true);
// 根据构造方法创建实例
Object demo = constructor.newInstance();

// 获取有参构造函数
Constructor constructor1 = demoClass.getDeclaredConstructor(String.class);
// 设置可以访问
constructor1.setAccessible(true);
// 根据构造方法创建实例
Object demo1 = constructor1.newInstance("I am String");

下面介绍一种通用的获取某个类的实例的方法

/**
     * 运用反射,通过默认的构造方法获取某个类的实例
     *
     * @param className
     * @return
     */
    public static Object getObjectInstance(String className, Object... args) {
        try {
            Class[] classes = new Class[args.length];
            for (int i = 0; i < args.length; i++) {
                classes[i] = args[i].getClass();
            }
            Class objClass = Class.forName(className);
            Constructor constructor = objClass.getDeclaredConstructor(classes);
            constructor.setAccessible(true);
            return constructor.newInstance(args);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return new Object();
    }

在运行时获取某个类的任何的变量并修改变量的值

下面的代码会告诉大家如何获取 Demo.java 的一个实例的变量 (priStr) 并对其赋值。

// 无参构造方法 new 的实例,里面的 priStr 值为 private_string
Demo demo = new Demo();
// 获取 demo 的 class
Class demoClass = demo.getClass();
// 获取 priStr 的 Field 
Field priStrField = demoClass.getDeclaredField("priStr");
// 设置可以访问
priStrField.setAccessible(true);

// 取 priStr 的值
// 要取的是 demo 这个实例的 priStr , 所以需要传入 demo
Object priStrValue = priStrField.get(demo); 

// 修改 priStr 的值
// 要修改的是 demo 这个实例, 所以需要传入 demo
priStrField.set(demo,"I am Private String");

下面介绍一种通用的获取属性的方法

/**
     * 获取某个变量的值
     *
     * @param obj       待取值的类
     * @param fieldName 待取值的变量的变量名
     * @return
     */
    public static Object getFieldValue(Object obj, String fieldName) {
        try {
            Field field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            return field.get(obj);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return new Object();
    }

    /**
     * 设置某个类的某个变量的值
     *
     * @param obj
     * @param fieldName
     * @param fieldValue
     * @return
     */
    public static boolean setFieldValue(Object obj, String fieldName, Object fieldValue) {
        try {
            Field field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(obj, fieldValue);
            return true;
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return false;
    }

在运行时执行某个类的任何方法

下面的代码会告诉大家如何获取 Demo.java 的一个方法(getPriStr) 并调用此方法。

// 无参构造方法 new 的实例,里面的 priStr 值为 private_string
Demo demo = new Demo();
// 获取 demo 的 class
Class demoClass = demo.getClass();

// 获取无参 getPriStr 对应的 Method 
Method priStrMethod = demoClass.getDeclaredMethod("priStr");
// 设置可以访问
priStrMethod.setAccessible(true);
// 调用此方法
Object priStr = priStrMethod.invoke(obj);

// 获取有参 getPriStr 对应的 Method 
Method priStrMethod1 = demoClass.getDeclaredMethod("priStr",String.class);
// 设置可以访问
priStrMethod1.setAccessible(true);
// 调用此方法
Object priStr1 = priStrMethod1.invoke(obj,"I am testParam");

下面介绍一种通用的调用对象的方法

/**
     * 调用某个方法
     * @param obj
     * @param methodName
     * @param args
     * @return
     */
    public static Object invokMethod(Object obj, String methodName, Object... args) {
        try {
            Class[] classes = new Class[args.length];
            for (int i = 0; i < args.length; i++) {
                classes[i] = args[i].getClass();
            }
            Method method = obj.getClass().getDeclaredMethod(methodName, classes);
            method.setAccessible(true);
            method.invoke(obj,args);
            return method.invoke(obj, args);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return new Object();
    }