package course; import java.lang.reflect.Constructor; /** * @author WanJl * @version 1.0 * @title Demo04 * @description 反射 * @create 2026/8/12 */ public class Demo04 { /* 反射 是Java语言提供的一种【运行时动态机制】,他允许程序在【运行的状态】中: 对 任意一个类 都能获取这个类的所有属性和方法(包括私有的成员private) 对 任意一个对象 都能够调用它的任意属性和方法。 这种 动态获取类的信息 + 动态调用对象方法 的功能 就是Java的反射机制。 普通的调用 反射调用 的区别: 获取类信息的时机: 普通调用(编译期)- 就是在编译器写死,代码写死。 反射调用(运行期)- 运行时才会动态获取 依赖关系: 普通调用(编译期)- 编译期强耦合,耦合度高 反射调用(运行期)- 运行时才管理,解耦 创建对象: 普通调用(编译期)- new 类名() 反射调用(运行期)- Class.forname() + newInstance() 调用方法: 普通调用(编译期)- 对象.方法名() 反射调用(运行期)- method对象.invoke(对象,参数) 灵活性方面: 普通调用(编译期)- 固定不变 反射调用(运行期)- 高度灵活,可以动态切换 一句话:普通方式是代码写死的,反射方式是动态的。反射是Java中各种框架的基础——Spring、MyBatis、Hibernate、JDBC驱动加载等等都离不开反射。 为什么需要反射? 如果没有反射,我们的程序需要使用某个类,就必须在编译期就要知道它的名字,然后使用new创建对象,使用. 调用方法。 这些在很多真实的场景中无法满足需求: 1、配置驱动:比如某个框架需要读取配置文件,文件里面写的时候字符串类型的类名,框架在运行的时候才会拿到这个字符串, 并且需要通过反射才能够把它翻译成真实的类并且进行实例化。 2、通用工具:写一个能够打印任意对象的全部属性的通用工具,调用方法后传入什么参数不确定,那么就只能通过反射来实现。 3、第三方类库的解耦:比如我们使用某个类库,Java只需要定义这个类库的规则(接口),类库自己实现具体的操作, 然后通过反射,在运行时动态加载,源码里面根本就不需要依赖某个具体的类。 注意:我们需要学习反射,目的是1、学习框架的时候帮助我们理解框架的底层源码。2、当我们进行架构设计、开发中间件程序的时候需要使用反射做解耦和通用适配。 至于日常的业务开发中,一定一定一定尽量不要使用反射。开发使用反射,就相当于玩游戏使用金手指(开挂) 1、使用反射可能破坏封装性 2、使用反射的效率要远比正常创建对象效率低的多,最低3~100倍 反射的工作机制: Java程序运行的时候,.class字节码文件会被类加载器(ClassLoader)加载进JVM(Java虚拟机),JVM会为每一个类创建 唯一的一个 java.lang.Class 对象。这个对象就是 “类的类模板”,里面记录类这个类的全部信息(类名、父类、接口、字段属性、方法、构造方法等等) 反射的一切操作,都是从这个Class对象出发的。拿到了Class之后,可以依次获取它的构造方法对象(Constructor)、方法对象(Method)、属性对象(Field) 再通过这些对象去创建这个类的实例对象,读写字段属性,调用方法... Person.java源码 --javac编译--> Person.class字节码 --类加载器(ClassLoader)加载-->Person的Class对象(类的类模板) 通过Person的Class对象 |-获取Person类的 构造方法对象Constructor --> newInstance()创建Person对象 |-获取Person类的 字段属性对象Field -->get/set 读写字段属性变量 |-获取Person类的 方法对象Method -->invoke()执行方法 反射涉及到的核心类: Class 类 java.lang.Class 表示一个类的模板,是反射的入口 Constructor 构造方法 java.lang.reflect.Constructor 表示一个构造方法,可以创建对象 Field 字段属性 java.lang.reflect.Field 表示一个成员变量,可以读写字段属性 Method 方法 java.lang.reflect.Method 表示一个成员方法,可以调用方法 Modifier 修饰符 java.lang.reflect.Modifier 解析修饰符(public/private/static等等) Parameter 参数 java.lang.reflect.Modifier 代表的是方法参数 */ public void printUtil(Object obj){ //输出这个类的所有属性名---就必须要使用反射来实现 } }