当前位置: 首页 > news >正文

全网最通俗易懂 Java 反射精讲!从入门到框架底层,一篇吃透反射核心

一、反射到底是什么?一句话讲透核心本质

在正式敲代码前,我们先把反射的本质掰碎了讲明白,这是学好反射的第一步!

先给大家一个最通俗的定义:反射,就是 Java 在程序运行时,能 “解剖” 一个类的技术!

不管这个类是 public 还是 private,不管你知不知道它的具体类型,只要拿到它的Class 对象,反射就能帮你:

  • 拿到这个类的构造方法(无参、有参、私有都能拿)
  • 拿到这个类的成员变量(public、private、protected 全能获取)
  • 拿到这个类的成员方法(公开方法、私有方法、静态方法全能调用)
  • 甚至能动态创建对象、动态赋值、动态执行方法

再简单点总结:正常写代码:先知道类名→创建对象→调用方法反射写代码:运行时才知道类名→动态拿构造→动态创对象→动态执行方法

这就是反射最大的魅力 ——动态性!也是所有框架能实现 “自动装配、依赖注入、注解解析” 的底层根基。

而反射的唯一核心,就是Class 对象! 所有反射操作,第一步必须先获取 Class 对象,没有它,反射寸步难行!


二、反射入门第一步:获取 Class 对象的 3 种方式(必考)

一切反射的起点,都是获取Class对象! Java 给我们提供了3 种标准方式,每一种都有固定使用场景,面试必问、开发必用!

方式 1:Class.forName ("全类名") —— 最常用、框架最爱

这是开发和框架中使用最多的方式,也是动态性最强的方式! 只需要传入类的全限定名(包名 + 类名),就能在运行时加载类,生成 Class 对象。

// 写法:Class.forName("完整包名+类名") Class<?> clazz = Class.forName("com.example.demo.Student");

核心特点

  • 运行时动态加载类,解耦极强
  • 不需要导入类,不需要创建对象,只需要字符串
  • Spring、MyBatis、JDBC 全用这种方式加载类

方式 2:对象名.getClass () —— 已有对象时用

如果你已经创建了这个类的对象,想通过对象反向获取 Class 对象,就用这种方式!

// 先创建对象 Student student = new Student(); // 通过对象获取Class Class<? extends Student> clazz = student.getClass();

核心特点

  • 必须先有对象,才能调用
  • 多用于方法参数是对象时,动态获取类型

方式 3:类名.class —— 明确知道类名时用

如果你明确知道要操作的类名,直接用类名点 class,最简单粗暴!

// 直接通过类名获取 Class<Student> clazz = Student.class;

核心特点

  • 代码最简单,最安全
  • 编译期就确定类型,没有异常
  • 适合写死类名的场景

高频易错点提醒(90% 的人都错过)

很多同学会写错:

// 错误写法!!! 对象名.class; // 正确写法 对象名.getClass();

对象只能调用 getClass () 方法,不能用.class 属性!.class 是的属性,不是对象的!


三、3 种获取 Class 方式的真实使用场景(实战必懂)

光会写代码没用,你得知道什么时候用哪种方式,这才是真正掌握反射!

场景 1:读取配置文件 → 必须用 Class.forName ()

这是框架最核心的使用场景! 比如 JDBC 驱动加载、Spring 读取 bean 配置、MyBatis 加载 Mapper,全是这种用法。

举个实战例子:

  1. 在 src 目录下创建配置文件bean.properties
# 配置类的全路径 className=com.example.demo.Student
  1. 主程序读取配置,动态加载类
// 1.创建Properties读取配置 Properties prop = new Properties(); // 2.读取配置文件 prop.load(Main.class.getClassLoader().getResourceAsStream("bean.properties")); // 3.获取配置的类名 String className = prop.getProperty("className"); // 4.动态加载Class对象(核心!) Class<?> clazz = Class.forName(className);

优势:改配置不用改代码,完全解耦!

场景 2:方法参数是对象 → 用 对象.getClass ()

当你在方法里接收一个对象,想获取它的 Class,就用这种方式:

// 形参是对象 public void getObjectClass(Student stu){ // 通过对象获取Class Class<? extends Student> clazz = stu.getClass(); }

场景 3:明确知道类名 → 直接用 类名.class

比如你就是要操作 Student 类,直接写:

Class<Student> clazz = Student.class;

如果在类内部,还可以用this.getClass()替代!


四、反射实战第一弹:动态获取构造器,创建对象

学会获取 Class 对象后,第一个实战操作:用反射创建对象! 不管是无参、有参、私有构造,反射全能搞定!

核心 API(Constructor 构造器对象)

  1. 获取 public 构造器
// 获取public无参构造 clazz.getConstructor(); // 获取public有参构造,指定参数类型 clazz.getConstructor(String.class, int.class);
  1. 获取任意权限构造器(包含私有!)
// 获取所有构造器,public/private都可以 clazz.getDeclaredConstructor();
  1. 通过构造器创建对象
构造器对象.newInstance(参数);

实战准备:先创建一个 Student 实体类

我们用 Lombok 简化代码,方便测试:

import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class Student { // 私有成员变量 private String name; private int age; }

案例 1:反射调用 public 无参构造创建对象

public class ReflectTest { public static void main(String[] args) throws Exception { // 1.获取Class对象 Class<Student> clazz = Student.class; // 2.获取无参构造器 Constructor<Student> constructor = clazz.getConstructor(); // 3.通过构造器创建对象 Student student = constructor.newInstance(); // 4.赋值测试 student.setName("张三"); student.setAge(18); System.out.println(student); } }

案例 2:反射调用 public 有参构造创建对象

public static void main(String[] args) throws Exception { // 1.获取Class Class<Student> clazz = Student.class; // 2.获取有参构造:String,int Constructor<Student> constructor = clazz.getConstructor(String.class, int.class); // 3.传入参数创建对象 Student student = constructor.newInstance("李四", 20); System.out.println(student); }

案例 3:暴力反射 —— 调用私有构造器(重点!)

如果构造方法是private的,正常代码根本用不了,但反射可以暴力破解! 只需要一行代码:setAccessible(true)

public static void main(String[] args) throws Exception { Class<Student> clazz = Student.class; // 1.获取私有构造器(用Declared!) Constructor<Student> constructor = clazz.getDeclaredConstructor(); // 2.暴力反射:关闭权限检查!!! constructor.setAccessible(true); // 3.创建对象 Student student = constructor.newInstance(); System.out.println(student); }

这就是反射的强大之处:无视权限!


五、反射实战第二弹:动态操作成员变量(Field)

不管成员变量是 public 还是 private,反射都能获取值、修改值

核心 API 区分(必背)

  • getField("变量名")只能获取 public 变量
  • getDeclaredField("变量名")获取所有权限变量(private 首选)

核心操作方法

// 1.暴力破解私有变量(必须加) field.setAccessible(true); // 2.给对象的变量赋值 field.set(对象, 值); // 3.获取对象的变量值 field.get(对象);

实战案例:反射操作私有成员变量

public static void main(String[] args) throws Exception { // 1.获取Class Class<Student> clazz = Student.class; // 2.获取私有成员变量 name(必须用Declared) Field nameField = clazz.getDeclaredField("name"); // 3.暴力反射 nameField.setAccessible(true); // 4.创建空对象 Student student = clazz.newInstance(); // 5.给私有变量赋值 nameField.set(student, "反射修改变量"); // 6.获取变量值 String name = (String) nameField.get(student); System.out.println("获取到的名字:" + name); }

运行结果:

获取到的名字:反射修改变量

正常代码无法访问 private 变量,但反射轻松搞定!Spring 依赖注入就是这么实现的!


六、反射实战第三弹:动态调用成员方法(Method)

这是反射最常用、最核心的功能! 不管是 public 方法、private 方法、有参方法、无参方法,反射都能动态执行!

核心 API 区分

  • getMethod("方法名", 参数类型):只能获取 public 方法
  • getDeclaredMethod("方法名", 参数类型):获取所有权限方法

核心执行方法

// 破解私有方法 method.setAccessible(true); // 执行方法:invoke(对象, 方法参数) method.invoke(对象, 参数...);

案例 1:反射调用 public 的 get/set 方法

public static void main(String[] args) throws Exception { // 1.获取Class Class<Student> clazz = Student.class; Student student = clazz.newInstance(); // 2.获取setName方法:参数是String Method setNameMethod = clazz.getMethod("setName", String.class); // 3.执行方法:给student对象赋值 setNameMethod.invoke(student, "反射调用方法"); // 4.获取getName方法并执行 Method getNameMethod = clazz.getMethod("getName"); String name = (String) getNameMethod.invoke(student); System.out.println("执行getName结果:" + name); }

案例 2:反射调用私有方法(暴力反射)

如果方法是private的,照样能调用:

// 1.获取私有方法 Method privateMethod = clazz.getDeclaredMethod("私有方法名"); // 2.暴力破解 privateMethod.setAccessible(true); // 3.执行方法 privateMethod.invoke(student);

Spring 的注解驱动、AOP、事务管理,底层全靠这个 invoke 方法!


七、反射核心必背规则(面试 + 考试高频)

这些知识点是面试官最爱问的,也是写反射代码不报错的关键,必须背下来!

  1. getXXX () 只能获取 public 权限构造器、变量、方法,只要带Field/Method/Constructor,都只能拿 public 的。

  2. getDeclaredXXX () 能获取所有权限private、默认、protected、public,全能获取,开发首选 Declared 系列

  3. 操作私有资源必须加 setAccessible (true)不加这行代码,直接报IllegalAccessException权限异常!

  4. 反射所有 API 都抛出编译时异常代码必须try-catch或者throws抛出,否则无法运行。

  5. Class.forName () 是框架解耦神器配置文件 + forName,实现无代码侵入,Spring 核心用法。

  6. 反射最大特点:运行时动态操作编译期不知道类型,运行时才确定,这是框架的灵魂。


八、反射所有核心 API 终极汇总(一张表搞定)

我把反射所有常用 API 整理成速查表,开发、面试直接对照用!

1. Class 类核心方法

方法功能API 方法
获取 public 无参构造getConstructor()
获取 public 有参构造getConstructor (参数类型...)
获取任意构造getDeclaredConstructor (参数类型...)
获取 public 成员变量getField (变量名)
获取任意成员变量getDeclaredField (变量名)
获取 public 成员方法getMethod (方法名,参数类型)
获取任意成员方法getDeclaredMethod (方法名,参数类型)
快速创建对象newInstance()

2. Constructor 构造器对象

方法作用
newInstance()创建对象
setAccessible(true)暴力破解私有构造

3. Field 成员变量对象

方法作用
set (对象,值)给变量赋值
get (对象)获取变量值
setAccessible(true)破解私有变量

4. Method 成员方法对象

方法作用
invoke (对象,参数)执行方法
setAccessible(true)破解私有方法

九、反射到底有什么用?不学框架就没用吗?

很多同学会问:我平时写业务代码,好像用不上反射啊?大错特错!你不用反射,但你天天在用的工具全是反射写的

反射的核心应用场景:

  1. Spring 框架:IOC 容器创建对象、依赖注入、注解解析
  2. MyBatis:Mapper 接口动态代理、参数赋值、结果封装
  3. SpringBoot:自动配置、类路径扫描、动态加载组件
  4. JDBC:加载数据库驱动
  5. 动态代理:JDK 动态代理底层就是反射
  6. 通用工具类:对象拷贝、属性赋值、注解校验

可以说:没有反射,就没有现在的 Java 生态!


十、全文总结:反射核心一句话复盘

最后帮大家用最简单的话,复盘整篇文章的核心:

  1. 反射 = 运行时解剖类,动态操作构造、变量、方法
  2. 一切反射从 Class 对象开始,3 种获取方式牢记场景
  3. getXXX 拿 public,getDeclared 拿全部
  4. 私有资源必须暴力反射:setAccessible (true)
  5. 构造器创建对象,Field 赋值,Method 执行方法
  6. 反射是框架底层核心,不学反射看不懂源码
http://www.cnnetsun.cn/news/2628675.html

相关文章:

  • 雾化器语音提示芯片方案:便携电池供电+低功耗WT588F02-8S-C
  • Mapillary Vistas数据集实战:用Python快速加载并可视化66类街景语义分割标签
  • 如何通过FPGA设计实现2.5G以太网芯片控制?
  • 高效搞定答辩文稿制作!okbiye 智能 AI PPT 助力学子完成毕业汇报创作
  • 保姆级教程:用U盘启动盘修复Win10的No Bootable Device和蓝屏重启
  • 企业如何通过Taotoken的APIKey管理与访问控制规范内部使用
  • C166架构中DPP寄存器的安全使用与性能优化
  • 单细胞数据分析避坑指南:你的细胞比例计算结果真的可靠吗?从Seurat对象到ggplot2绘制的全流程检查点
  • 当Kon-Boot遇上Win10微软账户:实测免费版行不通?试试这个创建新管理员的隐藏技巧
  • AI Agent Harness Engineering 的隐私保护:数据安全最佳实践
  • 告别手动对焦!用Python+OpenCV实战图像清晰度评价(附Sobel、Laplace等8种算法对比)
  • 【助睿实验指导】浏览器用户行为分析与流失预测-数据加工
  • 时间序列建模避坑指南:你的ACF/PACF分析可能从一开始就错了
  • 【YOLO目标检测全栈实战】89 跨模态YOLO:用CLIP给检测结果“开天眼”
  • 我的大一下
  • 用DeepXDE搞定薛定谔方程:一个Python物理信息神经网络(PINN)的保姆级实践
  • 用Python+OpenCV复刻《二十年后》经典场景:手把手教你实现人脸识别与‘二十年对比’特效
  • NQ486固态MT29F16T08GSLDHL8-QM:D
  • 路由器是怎么知道往哪儿送的?揭秘“导航大师“的聪明大脑
  • 27考研米鹏有道|小黑全程班网课PDF
  • NPU模拟器搭建与深度学习硬件加速优化实践
  • Arduino与PIR传感器构建智能运动检测系统:从原理到实战
  • redis_点评(24.好友关注—实现关注推送页面的「滚动分页查询」)
  • 智能戒指技术解析:医疗监测与人机交互的硬件与算法
  • 单片机串口通信异常问题分析与解决方案
  • 别再只看Top-1了!用Python实战解析Rank-1与Rank-5正确率,帮你更懂模型真实能力
  • 嵌入式文件系统断电损坏问题与解决方案
  • 别再为Qt程序中文输入发愁了!一份通用的 fcitx5-qt 插件编译指南(覆盖Qt5/Qt6)
  • 从时序图到实战:拆解ZYNQ VDMA的Line Buffer,搞定视频流拼接与缩放
  • 如何快速清理重复图片:开源智能去重工具的终极指南