Java反射

72 阅读6分钟

1.什么是反射

1.1 反射概念

  • 反射是一种机制,利用该机制可以在程序运行过程中对类进行解剖,并得到并操作类中的方法,属性,构造方法等成员。

01.反射.png

  • 简单理解为:
    • 通过反射可以获取类里的所有成员:获取类的信息、所有构造方法、所有普通方法、所有成员变量等等
    • 通过反射可以操作类里的所有成员:反射方式调用类的构造方法、普通方法、成员方法

1.2 什么时候需要使用反射?

  • 当使用普通方式不能调用的时候,可以考虑使用反射调用。
  • 例如想要强制调用私有方法,普通调用方式调不了,使用反射就可以了(原则上不建议强制调用private方法)。

2. 反射的使用

2.1 获取Class对象★

API说明:java.lang.Class:是类的信息对象。Java中一切皆对象,那么一个类的信息也被封装成了对象--Class对象。一切反射操作的前提,必须先获取类的Class对象。

2.1.1 获取Class的方式

  • 通过class指令获取:Class clazz = 类名.class
  • 通过getClass()获取:Class clazz = 对象.getClass() Object类提供了getClass方法,用于获取类的Class
  • 通过Class.forName获取:Class clazz = Class.forName("全限定类名")

全限定类名:即包名+类名。例如:java.lang.Object是全限定类名, Object是简单类名

2.1.2 Class的常用方法

方法说明
String getName()获取类的全限定类名
String getSimpleName()获取简单类名
Constructor[] getConstructors()获取所有public构造方法
Constructor[] getDeclaredConstructors()获取所有构造方法
Constructor getConstructor(Class... argTypes)获取指定类型形参的public构造方法
Constructor getDeclaredConstructor(Class... argTypes)获取指定类型形参的构造方法
Method[] getMethods()获取所有public的成员方法
Method[] getDeclaredMethods()获取所有成员方法
Method getMethod(String name,Class...argTypes)获取指定名称和形参类型的public方法
Method getDeclaredMethod(String name,Class...argTypes)获取指定名称和形参类型的方法
Field[] getFields()获取所有public成员变量
Field[] getDeclaredFields()获取所有成员变量
Field getField(String name)获取指定名称的public成员变量
Field getDeclaredField(String name)获取指定名称的成员变量

2.2 使用示例

2.2.1 准备一个User类

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

    public User() {
    }

    public User(String name) {
        this.name = name;
    }

    private User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void eat(String food){
        System.out.println("吃" + food);
    }

    private String sleep(){
        System.out.println("睡");
        return "睡爽了";
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

2.2.2 操作Class示例

/**
 * 获取User的Class对象
 */
public class Demo01Class {

    @Test
    public void testClass1() throws ClassNotFoundException {
        //1. 类名.class
        Class<User> clazz = User.class;

        System.out.println(clazz);
    }
    
    @Test
    public void testClass2() throws ClassNotFoundException {
        //2. 对象.getClass()
        User user = new User();
        Class clazz = user.getClass();

        System.out.println(clazz);
    }
    
    @Test
    public void testClass3() throws ClassNotFoundException {
        //3. Class.forName("")
        Class clazz = Class.forName("demo02_reflect.User");

        System.out.println(clazz);
    }
}

3. 反射操作构造方法

3.1 API说明

3.1.1 Constructor类

  • java.lang.reflect.Constructor:构造方法信息封装成的对象。它提供了以下常用方法:
方法说明
newInstance(Object... args)反射调用构造方法并传入实参,得到创建的实例对象
setAccessible(boolean flag)如果传参true,表示强制允许反射调用。即使是private也允许访问
  • 从Class里获取Constructor对象的API:
方法说明
Constructor[] getConstructors()获取所有public构造方法
Constructor[] getDeclaredConstructors()获取所有构造方法
Constructor getConstructor(Class... argTypes)获取指定类型形参的public构造方法
Constructor getDeclaredConstructor(Class... argTypes)获取指定类型形参的构造方法

3.1.2 反射调用构造方法的步骤

  • 一切反射的前提,是获取类的Class字节码对象

  • 反射调用构造方法的步骤

    1. 获取类的Class对象:前边有API
    2. 从Class对象中获取构造方法的Constructor对象:前边有API
    3. 反射调用Constructor,得到类实例对象

3.2 使用示例

public class Demo02Constructor {

    /**
     * 通过User的无参构造,得到一个User实例对象
     */
    @Test
    public void testConstructor() throws Exception {
        //1. 获取类的Class对象
        Class clazz = User.class;
        //2. 获取构造方法对象
        Constructor constructor = clazz.getConstructor();
        //3. 反射调用构造方法,生成类实例对象
        Object user = constructor.newInstance();

        System.out.println(user);
    }


    /**
     * 通过User的有参构造,得到一个User实例对象
     *  参数:String, Integer,并且方法是private
     *
     */
    @Test
    public void testConstructor2() throws Exception {
        //1. 获取类的Class对象
        Class clazz = User.class;
        //2. 获取类的有参构造方法对象
        Constructor constructor = clazz.getDeclaredConstructor(String.class, Integer.class);

        constructor.setAccessible(true);

        //3. 反射调用构造方法
        Object user = constructor.newInstance("tom", 45);

        System.out.println(user);
    }
}

4. 反射操作成员方法

4.1 API说明

4.1.1 Method类

  • java.lang.reflect.Method:普通方法信息封装成的对象。它提供了以下常用方法:
方法说明
invoke(Object instance, Object... args)反射调用方法并传入实参,得到方法的返回值
setAccessible(boolean flag)如果传参true,表示强制允许反射调用。 即使是private也允许访问
  • 从Class里获取Method对象的API:
方法说明
Method[] getMethods()获取所有public的成员方法
Method[] getDeclaredMethods()获取所有成员方法
Method getMethod(String name,Class...argTypes)获取指定名称和形参类型的public方法
Method getDeclaredMethod(String name,Class...argTypes)获取指定名称和形参类型的方法

4.1.2 反射调用普通方法的步骤

  • 一切反射的前提,是获取类的Class字节码对象

  • 反射调用普通方法的步骤

    1. 获取类的Class对象
    2. 从Class中获取普通方法的Method对象
    3. 反射调用Method,得到方法的返回值

4.2 使用示例

public class Demo03Method {

    /**
     * 调用User对象(user1)的eat方法:参数 String
     */
    @Test
    public void testMethod() throws Exception {
        //普通方式调用:
        User user1 = new User();
        User user2 = new User();
        User user3 = new User();
        User user4 = new User();
        //user1.eat("泡面");

        //反射方式调用
        //1. 获取类的Class
        Class clazz = User.class;
        //2. 获取方法的Method对象
        Method method = clazz.getMethod("eat", String.class);
        //3. 反射调用method(执行方法)
        Object result = method.invoke(user1, "泡面");
        System.out.println(result);
    }

    /**
     * 调用User的sleep方法:参数String
     */
    @Test
    public void testMethod2() throws Exception {
        User user = new User();
        //user.sleep("");

        //1.获取类的字节码对象Class
        Class clazz = User.class;
        //2. 获取方法的Method对象
        Method method = clazz.getDeclaredMethod("sleep", String.class);

        method.setAccessible(true);

        //3. 反射调用方法
        Object result = method.invoke(user, "tom");
        System.out.println(result);
    }
}

5. 反射操作成员变量

5.1 API说明

5.1.1 Field类

  • java.lang.reflect.Field:类成员变量信息封装成的对象。它提供了以下常用方法:
方法说明
get(Object instance)获取实例对象的成员变量值
set(Object instance, Object value)设置实例对象的成员变量值
setAccessible(boolean flag)如果传参true,表示强制允许反射调用。即使是private也允许访问
  • 从Class里获取Field对象的API:
方法说明
Field[] getFields()获取所有public成员变量
Field[] getDeclaredFields()获取所有成员变量
Field getField(String name)获取指定名称的public成员变量
Field getDeclaredField(String name)获取指定名称的成员变量

5.1.2 反射操作成员变量的步骤

  • 一切反射的前提,是获取类的Class字节码对象

  • 反射操作属性的步骤

    1. 获取类的Class对象
    2. 从Class中获取属性成员变量的Field对象
    3. 反射调用Field,设置属性字段值,或者获取属性成员变量值

5.2 使用示例

public class Demo04Field {

    /**
     * 操作User的age属性值
     */
    @Test
    public void testField1() throws Exception {
        //普通方式
        User user = new User();
        User user1 = new User();
        //user.age = 45;
        //System.out.println(user.age);


        //反射方式
        //1. 获取类的Class
        Class clazz = User.class;
        //2. 获取Field对象
        Field field = clazz.getField("age");
        //3. 设置属性值
        field.set(user, 50);
        //4. 获取属性值
        Object value = field.get(user);
        System.out.println(value);
    }

    /**
     * 操作User的name属性  private
     */
    @Test
    public void testField2() throws Exception {
        User user = new User();

        //1. 获取类的Class
        Class clazz = User.class;
        //2. 获取Field
        Field field = clazz.getDeclaredField("name");

        field.setAccessible(true);

        //3. 设置属性值
        field.set(user, "jerry");

        //4. 获取属性值
        Object value = field.get(user);
        System.out.println(value);
    }
}