反射【反射是框架的灵魂】
1. 概述
在程序运行过程中可以对任意类型中的任意资源进行操作,这种动态获取或操作资源的行为就叫做反射。
反射就是正向运行的逆向过程。
正向:编写源代码 --> 编译成字节码文件 --> JVM 加载字节码文件启动运行 --> 创建对象或者类名 --> 调用对应的方法和属性运行
反射:代码运行的过程中直接获取到字节码文件对象 --> 通过字节码文件对象获取对应的元素的对象 --> 通过元素对象调用对应的方法开始执行功能
字节码文件对象:字节码文件在内存中的对象表现形式,Java 中使用一个 Class 类对字节码文件这种事物进行相关特征和行为的描述 Class 的对象就是 JVM 加载字节码文件的体现
JVM 加载 .class 文件的处理机制:
JVM 通过类加载器将字节码文件以对象的形式加载到内存中【在方法区中创建了一个 Class 类型的对象】,JVM 加载之后对这个 Class 对象的内容进行分类管理【属性、成员方法构造方法】,JVM 分完类发现内容各自有各自的共同特征,把这些类别内容分别的进行描述形成对应的类型【属性对应的类型 Filed、方法对应的类 Method、构造对应的 Constract 】JVM 把管理者三个类的权限给Class对象来管理。
反射对象操作的元素除了对应的类对象还有 Filed 对象, Method 对象, Constract 对象。
2. Class 类型
class ClassName {
Field[] fields; // 成员变量 Field 类型数组,成员变量有多个,一个或者没有
Constructor[] constructors; // 构造方法 Constructor 类型数组,构造方法至少有一个,可能有多个
Method[] methods; // 成员方法 Method 类型数组,成员方法可以是一个,多个或者没有
}
2.1 Class 类型获取对象方式
Class<?> Class.forName( /*全限定类名*/ )
全限定类名:完整的包名+ 类名
这是 Class 类提供的静态成员方法,根据调用者提供的完整包名 + 类名,获取对应类的 Class 对象。Class<? extends T> 任意类对象.getClass();
这是 Object 类提供的
Class<T> 类名.class
通过 Java 中任意的类名,获取其属性内容。此方式一般用于明确通过反射方式获得方法时填写参数类型时。
2.2 代码演示
package com.exercise.demo;
/**
* @author: 85409 2023/3/10 23:20
* @description: TODO
*/
public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException {
// Class<?> Class.forName( /*全限定类名*/ )
Class<?> clazz1 = Class.forName("com.exercise.demo.Person");
System.out.println(clazz1);
// Class<? extends T> 任意类对象.getClass();
Person person = new Person();
Class<? extends Person> clazz2 = person.getClass();
System.out.println(clazz2);
// Class<T> 类名.class
Class<Person> clazz3 = Person.class;
System.out.println(clazz3);
}
}
结果:
3. Constructor 构造方法类型
3.1 概述
通过类对象获取构造方法时,通过【参数列表,参数数据类型, 参数个数, 参数顺序】来区分的!
3.2 通过 Class 对象获取对应的 Constructor 构造方法对象
Constructor[] getConstructors();
获取当前 Class 对应数据类型的所有【非私有化】构造方法对象数组
Constructor[] getDeclaredConstructors();
【暴力反射】获取 Class 对应数据类型的所有构造方法对象数组,包括私有化
Constructor getConstructor(class... parmeterTypes);
获取当前 Class 对应数据类型的指定构造方法对象数组【非私有化】
【class… parmeterTypes】 是 程序员想要获取的构造方法对应的参数类型的Class 类型。Constructor getDeclaredConstructor(Class... parameterTypes);
【暴力反射】获取当前 Class 对应数据类型的指定构造方法对象数组【包括私有化】
3.3 代码演示
package com.exercise.demo;
import java.lang.reflect.Constructor;
import java.util.Arrays;
/**
* @author: 85409 2023/3/11 14:20
* @description: TODO
*/
public class Demo2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
Class<?> clazz = Class.forName("com.exercise.demo.Person");
// 获得 Person 类对象中所有构造方法数组不包括私有化构造方法
Constructor<?>[] c1 = clazz.getConstructors();
Arrays.stream(c1).forEach(System.out::println);
System.out.println();
// 【暴力反射】获得 Person 类对象中所有构造方法数组包括私有化构造方法
Constructor<?>[] c2 = clazz.getDeclaredConstructors();
Arrays.stream(c2).forEach(System.out::println);
System.out.println();
// 获得参数类型为 String int float 的指定构造方法对象
Constructor<?> c3 = clazz.getConstructor(String.class, int.class, float.class);
System.out.println(c3);
System.out.println();
// 【暴力反射】获得参数类型为 String, int 类型的指定私有化构造方法对象
Constructor<?> c4 = clazz.getDeclaredConstructor(String.class, int.class);
System.out.println(c4);
}
}
结果
3.4 Constructor 对象实例化对象操作
Object newInstance(Object... parameterValues);
该方法是用来实例化对象操作的方法,通过 Constructor 构造方法对象调用 newInstance () 方法,参数为构造方法对象对应的实际参数
3.5 代码演示
对于私有化资源需要设置相应权限,对应方法 setAccessible ( true )
true 表示相应私有化资源可以使用, false 表示相应私有化资源不可以使用
如果没有设置权限对于私有化资源 会报错 【IllegalAccessException】非法权限异常
// c3 为非私有化构造方法对象 参数类型为 String, int, float
Person p1 = (Person) c3.newInstance("张三", 16, 15000);
System.out.println(p1);
// c4 为私有化构造方法对象 参数类型为 String, int
// 私有化方法不能直接使用 需要打开权限 对应方法 setAccessable( true )
// true 表示可以使用该私有方法 false 表示不可以使用该私有化方法
c4.setAccessible(true);
Person p2 = (Person) c4.newInstance("李四", 25);
System.out.println(p2);
结果:
4. Method 方法类型 【重点】
4.1 概述
通过类对象获取 Method 类的对象,相关方法关注点是【方法名】和【形式参数列表】
4.2 通过 Class 对象获取对应的 Method 成员方法对象
Method[] getMethods ();
获取当前 Class 对应数据类型的所有【非私有化】成员方法对象数组,包括父类继承给子类的成员方法
Method[] getDeclaredMethods();
【暴力反射】获取当前 Class 对应数据类型的所有成员方法对象数组,包括私有化成员方法,不包括父类继承给子类的方法
Method getMethod(String methodName, Class... parameterTypes);
获取当前 Class 对应数据类型的指定名称(methodName)成员方法
【Class… parameterTypes】获取对应成员方法参数的类型的 Class 类型对象
无法获取私有化的成员方法
Method getDeclaredMethod(String methodName, Class... parameterTypes);
【暴力反射】获取当前 Class 对应数据类型的指定名称(methodName)成员方法
【Class… parameterTypes】获取对应成员方法参数的类型的 Class 类型对象
私有方法也可以获取
4.3 代码演示
package com.exercise.demo;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* @author: 85409 2023/3/11 15:38
* @description: TODO
*/
public class Demo3 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
Class<?> clazz = Class.forName("com.exercise.demo.Person");
// 获得对应类内所有成员方法,包括父类继承给子类的成员方法,不包括私有方法
Method[] m1 = clazz.getMethods();
Arrays.stream(m1).forEach(System.out::println);
System.out.println();
// 【暴力反射】 获得对应类内的所有成员方法,包括私有化成员方法,返回值类型为 Method类型数组
Method[] m2 = clazz.getDeclaredMethods();
Arrays.stream(m2).forEach(System.out::println);
System.out.println();
// 获得对应类内指定名称的非私有化成员方法
Method m3 = clazz.getMethod("setName", String.class);
System.out.println(m3);
System.out.println();
// 获得对应类内指定名称的成员方法,包括私有化成员方法
Method m4 = clazz.getDeclaredMethod("test");
System.out.println(m4);
System.out.println();
}
}
结果:
4.4 Method 成员方法对象执行对应方法操作
通过 Method 对象使用以下方法,执行对应方法运行目标
Object invoke(Object obj, Object... parameterValues);
参数 obj 为当前执行方法对应类的实例化对象,静态成员变量可以直接传参 null
参数 Object… parameterValues 为当前执行方法对应的实际参数
【细节】
invoke 方法返回值类型 Object 类型,返回值为当前方法的返回值,如果方法没有返回值,那么invoke 返回值为 null
// 对应类的实例化对象 obj
Object obj = clazz.getConstructor().newInstance();
Object o1 = m3.invoke(obj, "张三");
// m3 对应方法没有返回值 o1 为null
System.out.println(o1);
// 通过反射获取一个静态方法
Method m5 = clazz.getMethod("show");
// 执行方法时传入参数为 null,该方法不需要实际参数,所以没有参数
m5.invoke(null);
5. Field 成员变量类型
5.1 概述
针对于类内的成员变量,唯一关注【成员变量名】
5.2 通过 Class 对象获取对应的 Field 成员变量对象
Field[] getFields();
获取当前 Class 对象类内的所有非私有化成员变量对象,返回值为 Field 类型数组
Field[] getDeclaredFields();
【暴力反射】获取当前 Class 对象类内所有成员变量对象,包括私有化成员变量,返回值为 Field 类型数组
Field getField(String fieldName);
获取当前 Class 对象类内指定(fieldName)非私有化成员变量对象,返回值为 Field 类型
Field getDeclaredField(String fieldName);
【暴力反射】获取当前 Class 对象类内指定(fieldName)成员变量对象,私有化成员变量也可以,返回值为 Field 类型
5.3 代码演示
package com.exercise.demo;
import java.lang.reflect.Field;
import java.util.Arrays;
/**
* @author: 85409 2023/3/11 17:52
* @description: TODO
*/
public class Demo4 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class<?> clazz = Class.forName("com.exercise.demo.Person");
// 获得对应类内所有成员变量对象,不包含私有化成员变量
Field[] f1 = clazz.getFields();
Arrays.stream(f1).forEach(System.out::println);
System.out.println();
// 【暴力反射】获得对应类内所有成员变量对象,包括私有化成员变量
Field[] f2 = clazz.getDeclaredFields();
Arrays.stream(f2).forEach(System.out::println);
System.out.println();
// 获得对应类内指定成员变量,不能获取私有化成员变量对象
Field f3 = clazz.getField("gender");
System.out.println(f3);
System.out.println();
// 【暴力反射】获取对应类内指定成员变量,可获取私有化成员变量对象
Field f4 = clazz.getDeclaredField("name");
System.out.println(f4);
System.out.println();
}
}
5.4 Field 对象对数据进行赋值和取值
总结为 set and get方法
void set(Object obj, Object value);
参数 obj 为对应的需要赋值的类对象
参数 value 为对应赋值成员变量的实际参数Object get(Object obj);
参数 obj 为对应需要赋值的类对象
返回值根据存储成员变量的实际参数为标准返回,统一为 Object 类型,最后可以进行类型强转因为这是一个没有危险的强转
5.5 代码演示
// 通过反射实例化 Person 类对象
Object obj = clazz.getConstructor().newInstance();
f3.set(obj, false);
System.out.println(obj);
// 设置 Field 对象 f4 的权限
f4.setAccessible(true);
f4.set(obj, "张三");
System.out.println(obj);
System.out.println(f3.get(obj));
System.out.println(f4.get(obj));
结果:
6. 暴力反射权限
void setAccessible(boolean flag);
反射操作中对于被 private 修饰的 Constructors,Methods,Fields 可以使用该方法赋予使用权限
flag 为 true 表示该成员变量,成员方法,或者构造方法可以调用使用
flag 为 false 表示没有使用权限public static void setAccessible(AccessibleObject[] array, boolean flag);
AccessibleObject 类工具方法,参数为 AccessibleObject 数组和权限标记
Field,Method,Constructor 都是 AccessibleObject 子类
演示 AccessibleObject 类工具方法
Field f5 = clazz.getDeclaredField("age");
AccessibleObject[] arr = {f4, f5};
AccessibleObject.setAccessible(arr, true);
f4.set(obj, "李四");
f5.set(obj, 26);
System.out.println(obj);
public static void setAccessible(AccessibleObject[] array, boolean flag);
AccessibleObject 类工具方法,参数为 AccessibleObject 数组和权限标记
Field,Method,Constructor 都是 AccessibleObject 子类
演示 AccessibleObject 类工具方法
Field f5 = clazz.getDeclaredField("age");
AccessibleObject[] arr = {f4, f5};
AccessibleObject.setAccessible(arr, true);
f4.set(obj, "李四");
f5.set(obj, 26);
System.out.println(obj);