反射:框架设计的灵魂

1、反射的概念

  • class是一切反射的根源,JAVA反射机制是在运行状态中,对于任何一个类,通过反射都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制
  • 使用反射的好处:
    1. 可以在程序运行过程中,操作这些对象。
    2. 可以解耦,提高程序的可扩展性。

java程序的三个阶段

  • Source源代码阶段:纯java代码(.java),当代码被编译以后变成字节码文件(.class)
  • Class类对象阶段:通过类加载器将字节码文件(.class)加载进内存
  • RunTime运行时阶段:程序创建对象

获取class对象三种方式

  • Class.forName(“类名”)
    • 多用于配置文件,将类名定义在配置文件中然后加载类
  • 类名.class
    • 多用于参数的传递
  • 对象.getClass()
    • 多用于对象的获取字节码的方式

在这里插入图片描述

获取class对象案例

person对象

public class Person
{
    private String name;
    private int age;

    public Person() {}

    public Person(String name, int age) 
    {
        this.name = name;
        this.age = age;
    }

    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
    public int getAge() {return age;}
    public void setAge(int age) {this.age = age;}
}

获取class对象案例

public class ReflectDemo1
{
    public static void main(String[] args) throws Exception 
    {
        //1.Class.forName("全类名")
        Class cls1 = Class.forName("com.domain.Person");
        System.out.println(cls1);//class com.domain.Person
        //2.类名.class
        Class cls2 = Person.class;
        System.out.println(cls2);//class com.domain.Person
        //3.对象.getClass()
        Person p = new Person();
        Class cls3 = p.getClass();
        System.out.println(cls3);//class com.domain.Person

        // == 比较三个对象
        System.out.println(cls1 == cls2);//true
        System.out.println(cls1 == cls3);//true
    }
}

结论

​ 同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。

2、Class对象功能

2.1 获取功能
  • 获取成员变量们

    • Field[] getFields() 获取所有public修饰的成员变量
    • Field getField(String name) 获取指定名称的 public修饰的成员变量
    • Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
    • Field getDeclaredField(String name) 获取指定名称的成员变量,不考虑修饰符
  • 获取构造方法们

    • Constructor<?>[] getConstructors() 获取所有public修饰的构造方法

    • Constructor getConstructor(Class<?>… parameterTypes) 获取指定名称public修饰的构造方法

    • Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法,不考虑修饰符

    • Constructor getDeclaredConstructor(Class<?>… parameterTypes) 获取指定名称的构造方法,不考虑修饰符

  • 获取成员方法们:

    • Method[] getMethods() : 获取所有public修饰的方法

    • Method getMethod(String name, Class<?>… parameterTypes) : 获取public修饰的指定名称的方法

    • Method[] getDeclaredMethods() 获取所有的方法,不考虑修饰符

    • Method getDeclaredMethod(String name, Class<?>… parameterTypes) 获取指定名称的方法,不考虑修饰符

  • 获取全类名

    • String getName()
2.2 功能案例

提供person实体类:

public class Person 
{
    private String name;
    private int age;
    
    public String a;
    protected String b;
    String c;
    private String d;

    public Person() {}

    public Person(String name, int age) 
    {
        this.name = name;
        this.age = age;
    }

    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
    public int getAge() {return age;}
    public void setAge(int age) {this.age = age;}
}
2.2.1 Field:成员变量

操作:

  • 设置值

    • void set(Object obj, Object value)
  • 获取值

    • get(Object obj)
  • 忽略访问权限修饰符的安全检查

    • setAccessible(true) : 暴力反射

测试filed类

import com.domain.Person;

import java.lang.reflect.Field;

public class ReflectDemo2 
{
    public static void main(String[] args) throws Exception 
    {
        //0.获取Person的Class对象
        Class personClass = Person.class;
        
       /*
        * @Descript 获取成员变量们
        * Field[] getFields(): 获取所有public修饰的成员变量
        * Field getField(String name): 获取指定名称的 public修饰的成员变量
		*
        * Field[] getDeclaredFields(): 获取所有的成员变量,不考虑修饰符
        * Field getDeclaredField(String name)
        */

        //1.Field[] getFields(): 获取所有public修饰的成员变量
        Field[] fields = personClass.getFields();
        for (Field field : fields) 
        {
            System.out.println(field);//public java.lang.String com.domain.Person.a
        }

        System.out.println("---------------");

        //2.Field getField(String name): 获取指定名称的 public修饰的成员变量
        Field a = personClass.getField("a");
        //获取成员变量a的值
        Person p = new Person();
        Object value1 = a.get(p);
        System.out.println(value); //null
        //设置a的值
        a.set(p,"张三");
        System.out.println(p);
        //Person{name='null', age=0, a='张三', b='null', c='null', d='null'}

        System.out.println("---------------");

        //3.Field[] getDeclaredFields(): 获取所有的成员变量,不考虑修饰符
        Field[] declaredFields = personClass.getDeclaredFields();
        for (Field declaredField : declaredFields)
        {
            System.out.println(declaredField);
            //private java.lang.String com.domain.Person.name
            //private int com.domain.Person.age
            //public java.lang.String com.domain.Person.a
            //protected java.lang.String com.domain.Person.b
            //java.lang.String com.domain.Person.c
            //private java.lang.String com.domain.Person.d
        }

        //4.Field getDeclaredField(String name): 获取指定名称的成员变量,不考虑修饰符
        Field d = personClass.getDeclaredField("d");
        //忽略访问权限修饰符的安全检查
        d.setAccessible(true);//暴力反射
        Object value2 = d.get(p);
        System.out.println(value2);//null
    }
}
2.2.2 Constructor:构造方法

创建对象:

  • T newInstance(Object… initargs)

  • 如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法

import com.domain.Person;

import java.lang.reflect.Constructor;

public class ReflectDemo3 
{
    public static void main(String[] args) throws Exception 
    {
        //0.获取Person的Class对象
        Class personClass = Person.class;
        
       /* @Description 获取构造方法们
        * Constructor<?>[] getConstructors() 获取所有public修饰的构造方法	
        * Constructor<T> getConstructor(class<?>... parameterTypes) 获取指定名称public修饰的构造方法
        *
        * Constructor<T> getDeclaredConstructor(class<?>... parameterTypes) 获取所有的构造方法,不考虑修饰符
        * Constructor<?>[] getDeclaredConstructors() 获取指定名称的构造方法,不考虑修饰符
        */
        
        //Constructor<T> getConstructor(类<?>... parameterTypes) 获取指定名称public修饰的构造方法
        Constructor constructor = personClass.getConstructor(String.class, int.class);
        System.out.println(constructor);//public com.domain.Person(java.lang.String,int)
        //创建对象
        Object person = constructor.newInstance("张三", 23);
        System.out.println(person);
        //Person{name='张三', age=23, a='null', b='null', c='null', d='null'}

        System.out.println("---------------");

        Constructor constructor1 = personClass.getConstructor();
        System.out.println(constructor1);//public com.domain.Person()
        //创建对象
        Object person1 = constructor1.newInstance();
        System.out.println(person1);
        //Person{name='null', age=0, a='null', b='null', c='null', d='null'}


        //使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法
        Object o = personClass.newInstance();
        System.out.println(o);
        //Person{name='null', age=0, a='null', b='null', c='null', d='null'}

    }
}
2.2.3 Method:方法对象

执行方法:

  • Object invoke(Object obj, Object… args)
public class ReflectDemo4 
{
    public static void main(String[] args) throws Exception 
    {
        //0.获取Person的Class对象
        Class personClass = Person.class;

      /*
       * Method[] getMethods() 获取所有public修饰的方法
       * Method getMethod(String name, Class<?>... parameterTypes) 获取public修饰的指定名称的方法

       * Method[] getDeclaredMethods获取所有的方法,不考虑修饰符
       * Method getDeclaredMethod(String name, Class<?>... parameterTypes获取指定名称的方法,不考虑修饰符
       */
        //获取指定名称的方法
        Method eat_method = personClass.getMethod("eat");
        Person p = new Person();
        //指定方法
        eat_method.invoke(p);//eat...

        Method eat_method2 = personClass.getMethod("eat",String.class);
        //执行方法
        eat_method2.invoke(p,"饭");//eat饭

        System.out.println("----------------");

        //获取所有public修饰的方法
        Method[] methods = personClass.getMethods();
        for (Method method : methods)
        {
            System.out.println(method);
            //获取方法名称
            String name = method.getName();
            System.out.println(name);
        }
    }
}

获取方法名称:

  • String getName():获取方法名
public class ReflectDemo4 
{
    public static void main(String[] args) throws Exception
    {
        //0.获取Person的Class对象
        Class personClass = Person.class;

        Person p = new Person();

        System.out.println("----------------");

        //获取所有public修饰的方法
        Method[] methods = personClass.getMethods();
        for (Method method : methods)
        {
            //获取方法名称
            String name = method.getName();
            System.out.println(name);
        }
    }
}
2.2.4 获取全类名
  • String getName()
public class ReflectDemo4 
{
    public static void main(String[] args) throws Exception 
    {
        //0.获取Person的Class对象
        Class personClass = Person.class;

        //获取类名
        String className = personClass.getName();
        System.out.println(className);//com.domain.Person
    }
}

3、黑马案例

需求:写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法

  • 实现:
    1. 配置文件
    2. 反射
  • 步骤:
    1. 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
    2. 在程序中加载读取配置文件
    3. 使用反射技术来加载类文件进内存
    4. 创建对象
    5. 执行方法

创建pro.properties文件:

className = com.domain.Person
methodName = eat

创建ReflectTest类:

public class ReflectTest {
    public static void main(String[] args) throws Exception {
        //可以创建任意类的对象,可以执行任意方法
        /*
        * 前提:不能改变该类的任何代码,可以创建任意类的对象,可以执行任意方法
        * */
        //1.加载配置文件
        //1.1创建Properties对象
        Properties pro = new Properties();
        //1.2加载配置文件,转换为一个集合
        //1.2.1获取class目录下的配置文件
        ClassLoader classLoader = ReflectTest.class.getClassLoader();
        InputStream in = classLoader.getResourceAsStream("pro.properties");
        pro.load(in);

        //2.获取配置文件中的定义的数据
        String className = pro.getProperty("className");
        String methodName = pro.getProperty("methodName");

        //3.加载该类进内存
        Class cls = Class.forName(className);
        //4.创建对象
        Object obj = cls.newInstance();
        //5.获取方法对象
        Method method = cls.getMethod(methodName);
        method.invoke(obj);
    }
}

4、反射工具类

public class ReflectUtils
{
    /**
     * @Descript 获取无参构造函数
     * @param className
     * @return
     */
    public static Object createObject(String className)
    {
        Class[] pareTyples = new Class[]{};
        Object[] pareVaules = new Object[]{};

        try
        {
            Class r = Class.forName(className);
            return createObject(r, pareTyples, pareVaules);
        }
        catch (ClassNotFoundException e)
        {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @Descript 获取一个参数的构造函数  已知className
     * @param className
     * @param pareTyple
     * @param pareVaule
     * @return
     */
    public static Object createObject(String className, Class pareTyple, Object pareVaule)
    {
        Class[] pareTyples = new Class[]{pareTyple};
        Object[] pareVaules = new Object[]{pareVaule};

        try
        {
            Class r = Class.forName(className);
            return createObject(r, pareTyples, pareVaules);
        }
        catch (ClassNotFoundException e)
        {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @Descript 获取构造方法 已知类
     * @param clazz
     * @return
     */
    public static Object createObject(Class clazz)
    {
        Class[] pareTyples = new Class[]{};
        Object[] pareVaules = new Object[]{};
        return createObject(clazz, pareTyples, pareVaules);
    }

    /**
     * @Descript 获取单个参数的构造方法 已知类
     * @param clazz
     * @param pareTyple
     * @param pareVaule
     * @return
     */
    public static Object createObject(Class clazz, Class pareTyple, Object pareVaule)
    {
        Class[] pareTyples = new Class[]{pareTyple};
        Object[] pareVaules = new Object[]{pareVaule};
        return createObject(clazz, pareTyples, pareVaules);
    }

    /**
     * @Description 获取多个参数的构造方法 已知className
     * @param className
     * @param pareTyples
     * @param pareVaules
     * @return
     */
    public static Object createObject(String className, Class[] pareTyples, Object[] pareVaules)
    {
        try
        {
            Class r = Class.forName(className);
            return createObject(r, pareTyples, pareVaules);
        }
        catch (ClassNotFoundException e)
        {
            e.printStackTrace();
        }
        return null;
    }

    /**
     *@Descript 获取构造方法
     * @param clazz
     * @param pareTyples
     * @param pareVaules
     * @return
     */
    public static Object createObject(Class clazz, Class[] pareTyples, Object[] pareVaules)
    {
        try
        {
            Constructor ctor = clazz.getDeclaredConstructor(pareTyples);
            ctor.setAccessible(true);
            return ctor.newInstance(pareVaules);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @Descript 执行无参方法
     * @param obj
     * @param methodName
     * @return
     */
    public static Object invokeInstanceMethod(Object obj, String methodName)
    {
        Class[] pareTyples = new Class[]{};
        Object[] pareVaules = new Object[]{};
        return invokeInstanceMethod(obj, methodName, pareTyples, pareVaules);
    }

    /**
     * @Descript 执行一个参数的方法
     * @param obj
     * @param methodName
     * @param pareTyple
     * @param pareVaule
     * @return
     */
    public static Object invokeInstanceMethod(Object obj, String methodName, Class pareTyple, Object pareVaule)
    {
        Class[] pareTyples = {pareTyple};
        Object[] pareVaules = {pareVaule};
        return invokeInstanceMethod(obj, methodName, pareTyples, pareVaules);
    }

    /**
     * @Descript 执行多个参数的方法
     * @param obj
     * @param methodName
     * @param pareTyples
     * @param pareVaules
     * @return
     */
    public static Object invokeInstanceMethod(Object obj, String methodName, Class[] pareTyples, Object[] pareVaules)
    {
        if (obj == null)
        {
            return null;
        }

        try {
            //调用一个private方法 //在指定类中获取指定的方法
            Method method = obj.getClass().getDeclaredMethod(methodName, pareTyples);
            method.setAccessible(true);
            return method.invoke(obj, pareVaules);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @Descript 执行无参静态方法
     * @param className
     * @param method_name
     * @return
     */
    public static Object invokeStaticMethod(String className, String method_name)
    {
        Class[] pareTyples = new Class[]{};
        Object[] pareVaules = new Object[]{};
        return invokeStaticMethod(className, method_name, pareTyples, pareVaules);
    }

    /**
     * @Descript 执行一个参数的静态方法
     * @param className
     * @param method_name
     * @param pareTyple
     * @param pareVaule
     * @return
     */
    public static Object invokeStaticMethod(String className, String method_name, Class pareTyple, Object pareVaule)
    {
        Class[] pareTyples = new Class[]{pareTyple};
        Object[] pareVaules = new Object[]{pareVaule};
        return invokeStaticMethod(className, method_name, pareTyples, pareVaules);
    }

    /**
     * @Descript 执行多个参数的静态方法
     * @param className
     * @param method_name
     * @param pareTyples
     * @param pareVaules
     * @return
     */
    public static Object invokeStaticMethod(String className, String method_name, Class[] pareTyples, Object[] pareVaules)
    {
        try
        {
            Class obj_class = Class.forName(className);
            return invokeStaticMethod(obj_class, method_name, pareTyples, pareVaules);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @Descript 无参静态方法
     * @param method_name
     * @return
     */
    public static Object invokeStaticMethod(Class clazz, String method_name)
    {
        Class[] pareTyples = new Class[]{};
        Object[] pareVaules = new Object[]{};
        return invokeStaticMethod(clazz, method_name, pareTyples, pareVaules);
    }

    /**
     * @Descript 一个参数静态方法
     * @param clazz
     * @param method_name
     * @param classType
     * @param pareVaule
     * @return
     */
    public static Object invokeStaticMethod(Class clazz, String method_name, Class classType, Object pareVaule)
    {
        Class[] classTypes = new Class[]{classType};
        Object[] pareVaules = new Object[]{pareVaule};
        return invokeStaticMethod(clazz, method_name, classTypes, pareVaules);
    }

    /**
     * @Descript 多个参数的静态方法 已知类
     * @param clazz
     * @param method_name
     * @param pareTyples
     * @param pareVaules
     * @return
     */
    public static Object invokeStaticMethod(Class clazz, String method_name, Class[] pareTyples, Object[] pareVaules)
    {
        try
        {
            Method method = clazz.getDeclaredMethod(method_name, pareTyples);
            method.setAccessible(true);
            return method.invoke(null, pareVaules);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @Descript 获取属性值
     * @param className
     * @param obj
     * @param filedName
     * @return
     */
    public static Object getFieldObject(String className, Object obj, String filedName)
    {
        try
        {
            Class obj_class = Class.forName(className);
            return getFieldObject(obj_class, obj, filedName);
        }
        catch (ClassNotFoundException e)
        {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @Descript 获取属性值
     * @param clazz
     * @param obj
     * @param filedName
     * @return
     */
    public static Object getFieldObject(Class clazz, Object obj, String filedName)
    {
        try
        {
            Field field = clazz.getDeclaredField(filedName);
            field.setAccessible(true);
            return field.get(obj);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * @Descript 设置属性值
     * @param clazz
     * @param obj
     * @param filedName
     * @return
     */
    public static void setFieldObject(Class clazz, Object obj, String filedName, Object filedVaule)
    {
        try
        {
            Field field = clazz.getDeclaredField(filedName);
            field.setAccessible(true);
            field.set(obj, filedVaule);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    /**
     * @Descript 设置属性值
     * @param className
     * @param obj
     * @param filedName
     * @param filedVaule
     */
    public static void setFieldObject(String className, Object obj, String filedName, Object filedVaule)
    {
        try
        {
            Class obj_class = Class.forName(className);
            setFieldObject(obj_class, obj, filedName, filedVaule);
        }
        catch (ClassNotFoundException e)
        {
            e.printStackTrace();
        }
    }

    /**
     * @Descript 静态变量获取值
     * @param className
     * @param filedName
     * @return
     */
    public static Object getStaticFieldObject(String className, String filedName)
    {
        return getFieldObject(className, null, filedName);
    }

    /**
     * @Descript 静态变量获取值
     * @param clazz
     * @param filedName
     * @return
     */
    public static Object getStaticFieldObject(Class clazz, String filedName)
    {
        return getFieldObject(clazz, null, filedName);
    }

    /**
     * @Descript 静态变量设置值
     * @param classname
     * @param filedName
     * @return
     */
    public static void setStaticFieldObject(String classname, String filedName, Object filedVaule)
    {
        setFieldObject(classname, null, filedName, filedVaule);
    }

    /**
     * @Descript 静态变量设置值
     * @param clazz
     * @param filedName
     * @return
     */
    public static void setStaticFieldObject(Class clazz, String filedName, Object filedVaule)
    {
        setFieldObject(clazz, null, filedName, filedVaule);
    }
}
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐