反射【反射是框架的灵魂】

反射【反射是框架的灵魂】

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);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值