反射

40 阅读6分钟

反射 Reflection 被视为动态语言的关键。

反射机制允许Java程序在运行时获取一个类的所有信息,包括任何定义的信息(成员变量,成员方法,构造方法等),并且可以对类的字段、方法、构造方法等进行操作。

Java虚拟机需要加载一个类,会变成 .class 文件(字节码文件),这里存的是类的所有的信息。Java可以通过反射将字节码文件变成一个对象: Class对象

先来看一个入门案例:

package com.qf.test_Reflection;
​
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
​
public class Demo1 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //1.将Person.class这个文件 变成一个class对象
        Class<Person> personClass = Person.class;
        System.out.println(personClass);
        //2.将Person类的无参构造方法变成一个对象 constructor
        Constructor<Person> constructor = personClass.getConstructor();
        //3.通过构造方法创建对象
        Person person = constructor.newInstance();
    }
}

总结步骤:

1.获取字节码文件对象(Person.class)

2.通过字节码文件获取构造方法对象 3.通过构造方法对象创建Person类对象

1.获取class对象

对于Java文件的Person.class文件,Java可以将它转换成Class对象,怎样获取这样的Class对象呢,主要有三种方式:

package com.qf.test_Reflection;
​
public class Demo1 {
    public static void main(String[] args) throws ClassNotFoundException {
        //1.类.class 获取类对应的 Class对象
        Class<Person> personClass = Person.class;
        System.out.println(personClass);
        //2.Class.forName() 通过类的 全限定名字 获取类对应的Class对象
        Class<?> aClass = Class.forName("com.qf.test_Reflection.Person");
        System.out.println(aClass);
        //3.通过 对象.getClass() 来创建对应的类的Class对象
        Class<? extends Person> aClass1 = new Person().getClass();
        System.out.println(aClass1);
    }
}

Class对象获取方法总结:

1.类.class 获取类对应的 Class对象

2.Class.forName() 通过类的 全限定名字 获取类对应的Class对象

3.通过 对象.getClass() 来创建对应的类的Class对象

这一步结束,已经获取了Person.class字节码文件的Class对象。Person.class字节码文件,存了好多的类的信息 接着就是要将这些信息取出来

2.获取Constructor对象

通过Class对象调用方法获取构造方法:

返回类型方法
Constructor<?>[]getConstructors()返回一个包含Constructor对象的数组,反射由此表示的类的所有 公共构造方法
Constructor<?>[]getDeclaredConstructors()返回一个包含 反映 Constructor对象表示的类声明的所有 Constructor对象 的数组
Constructor<T>getConstructor(类<?>... parameterTypes)返回一个 Constructor对象,该对象反映 Constructor对象表示的类的指定的公共 函数。
Constructor<T>getDeclaredConstructor(类<?>... parameterTypes)返回一个 Constructor对象,该对象反映 Constructor对象表示的类或接口的指定 函数。

以上是Class对象调用,获取构造方法的方法

方法名意义
newInstance(Object... initargs)使用此 Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例。

以上是Constructor调用,创建新实例的方法

package com.qf.test_Reflection;
​
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
​
public class Demo2 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //1.获取类的Class对象
        Class<Person> personClass = Person.class;
        //2.通过Class对象获取下面的构造方法对象
        //(1)获取 所有的公开的 构造方法
        Constructor<?>[] constructors = personClass.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
        System.out.println("==========");
        //(2)获取 所有的 构造方法
        Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }
        System.out.println("==========");
        //(3)获取 无参公开的构造方法(一个公开的构造方法)//多个参数 中间使用,隔开即可
        Constructor<Person> constructor = personClass.getConstructor(null);
        System.out.println(constructor);
        System.out.println("==========");
        //(4)获取 获取一个任意的构造方法
        Constructor<Person> declaredConstructor1 = personClass.getDeclaredConstructor(String.class,int.class);
        System.out.println(declaredConstructor1);
        System.out.println("==========");
        //构造方法已经获取,接下来咋办?   使用构造方法对象 创建实例
        Person person = declaredConstructor1.newInstance("绫华",18);
        System.out.println(person.name);
        System.out.println(person.age);
    }
}

3.获取Method对象

返回值方法
方法[]getMethods()返回一个包含方法对象的数组,反射由此对象表示的类或接口的所有公共方法,包括那些由类或接口和那些从超类和超接口继承的声明。
方法[]getDeclaredMethods()返回一个包含方法对象的数组,反射的类或接口的所有声明的方法,通过此 对象表示的,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。
方法getMethod(String name, 类<?>... parameterTypes)返回一个 方法对象,它反映此对象表示的类或接口的指定的公共成员方法 。
方法getDeclaredMethod(String name, 类<?>... parameterTypes)返回一个 方法对象,它反映此对象表示的类或接口的指定声明的方法。

以上是Class对象调用,获取方法的方法

方法名意义
Object invoke(Object obj, Object ...args);使用方法对象调用invoke,方法会执行的

以上是Method对象调用方法 方法会执行

package com.qf.test_Reflection;
​
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
​
public class Method11 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //1.获取Class对象
        Class<Person> personClass = Person.class;
        //2.通过Class对象获取下面的方法对象
        //(1)获取所有的公开的方法 和父类公开的方法
        Method[] methods = personClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("==========");
        //(2)获取所有的方法
        Method[] declaredMethods = personClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }
        System.out.println("==========");
        //(3)获取一个任意的方法(自己的或父类的)
        Method play = personClass.getMethod("play",String.class,int.class);
        System.out.println(play);
        System.out.println("==========");
        //(4)获取一个私有化的方法(自己的)
        Method eat = personClass.getDeclaredMethod("eat",String.class);
        System.out.println(eat);
        System.out.println("==========");
        //获取方法之后,方法调用
        //第一个参数 此方法在哪个对象下面
        //第二个参数 是方法有可能有参数,传的是实参
        Person person = personClass.getConstructor().newInstance();
​
        play.invoke(person,"胡桃",17);
        //可以通过暴力反射 设置方法或者属性的权限问题
        eat.setAccessible(true);
        eat.invoke(person,"绫华");//私有方法
    }
}

4.获取Field对象

返回值方法
Field[]getFields()返回一个包含Field对象的数组,反射由此对象表示的类或接口的所有可访问的公共字段(属性)。
Field[]getDeclaredFields()返回一个包含Field对象的数组,反映此对象表示的类或接口声明的所有字段。
FieldgetField(String name)返回一个 Field对象,它反映此对象表示的类或接口的指定公共成员字段。
FieldgetDeclaredField(String name)返回一个 Field对象,它反映此对象表示的类或接口的指定已声明字段。

以上是Class对象调用的方法,获取属性(字段)的方法

返回值方法
voidset(Object obj, Object value) 将value值赋值给obj下面的属性

以上是Field对象调用的方法,为属性赋值

package com.qf.test_Reflection;
​
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
​
public class Field11 {
    public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //获取Class对象
        Class<Person> personClass = Person.class;
        //1.获取所有的公开的属性对象
        Field[] fields = personClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("==========");
        //2.获取所有的属性对象
        Field[] declaredFields = personClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }
        System.out.println("==========");
        //3.获取单个公开的属性对象
        Field name = personClass.getField("name");
        System.out.println(name);
        System.out.println("==========");
        //4.获取单个任意的属性对象
        Field sex = personClass.getDeclaredField("sex");
        System.out.println(sex);
        System.out.println("==========");
        //属性获取完以后?  赋值
        Person person = personClass.getConstructor().newInstance();
        name.set(person,"派蒙");
        System.out.println(name.get(person));
        sex.setAccessible(true);
        sex.set(person,true);//私有变量,暴力反射 设置方法或者属性的权限问题
        System.out.println(sex.get(person));
    }
}