反射的初步理解

在正常写代码时,我们习惯了"对象.方法() "这种顺理成章的调用方式。但反射完全把这个逻辑反转了过来。


1. 核心思维转换:从"主动驱动"到"被动执行"

  • 常规编程(正向思维) :你手里拿着一个"遥控器(对象)",按下了上面的"按键(方法)"。

    • 语法:遥控器.按下按键("参数")
  • 反射编程(逆向思维) :你手里直接拿着一个孤零零的"按键(Method)",然后你去找个"遥控器(对象)"把它装上去,再按下去。

    • 语法:按键.执行(遥控器, "参数")

invoke 这个单词在英语里就是"唤醒、祈求、执行 "的意思。在 Java 中,invoke 就是那个**"按下去"**的动作。


2. 极简实战:用代码看透 invoke

假设我们有一个极其简单的 Programmer(程序员)类:

Java

csharp 复制代码
public class Programmer {
    
    // 1. 普通的实例方法(每个人写的语言不同)
    public void typeCode(String language) {
        System.out.println("正在狂敲代码,语言是:" + language);
    }

    // 2. 静态方法(全人类通用的规则,不需要实例化)
    public static void drinkCoffee() {
        System.out.println("吨吨吨...补充能量");
    }
}

现在,我们来看看"常规调用"和"用 invoke 强行调用"到底有什么区别。

场景 A:调用普通的实例方法 (typeCode)

【常规写法】

Java

ini 复制代码
Programmer xiaoMing = new Programmer();
xiaoMing.typeCode("Java"); // 正常人这么写

【反射 invoke 写法】

Java

arduino 复制代码
// 1. 拿到"按键" (获取 Method 对象)
Class<?> clazz = Programmer.class;
Method typeMethod = clazz.getDeclaredMethod("typeCode", String.class);

// 2. 准备"遥控器" (准备实例对象)
Object xiaoMing = clazz.newInstance(); // 相当于 new Programmer()

// 3. 按下按键!(执行 invoke)
// 参数 1:必须传入 xiaoMing,因为你要明确是"哪一个程序员"在敲代码!
// 参数 2:"Java" 就是传给 typeCode 方法的实际参数。
typeMethod.invoke(xiaoMing, "Java"); 

深入理解

如果你写成 typeMethod.invoke(null, "Java"),JVM 直接就会抛出 NullPointerException 或者 IllegalArgumentException。为什么?因为 JVM 会骂人:"你让我执行敲代码的动作,但你没告诉我是谁在敲!我怎么敲?"

场景 B:调用静态方法 (drinkCoffee)

【常规写法】

Java

scss 复制代码
Programmer.drinkCoffee(); // 静态方法直接用类名调,不需要 new 对象

【反射 invoke 写法】

Java

java 复制代码
// 1. 拿到"按键"
Method coffeeMethod = clazz.getDeclaredMethod("drinkCoffee");

// 2. 按下按键!(执行 invoke)
// 划重点:第一个参数传了 null!
// 为什么?因为喝咖啡在这个类里是 static 的,它不属于任何一个具体的程序员。
coffeeMethod.invoke(null); 

深入理解

当你把第一个参数传 null 时,就是在明确告诉 JVM:"这个方法是个公共设施(静态方法),你不需要找具体的对象,直接给我执行它原本的逻辑就行了。"


3. invoke 的底层到底干了什么?(JVM 视角)

作为一个了解过 JVM 原理的开发者,你可以这样理解 invoke

正常调用 xiaoMing.typeCode() 时,编译器在编译期 就会检查 xiaoMing 是不是 Programmer,有没有这个方法。

invoke 是在运行期 ,绕过了编译器的所有安全检查。当你调用 typeMethod.invoke(xiaoMing, "Java") 时:

  1. JVM 内部会进行类型校验:检查 xiaoMing 这个对象到底包不包含 typeMethod 这个方法的指令码。
  2. 检查你传的参数 "Java" 的类型和数量对不对得上。
  3. 如果都对,JVM 会动态地把执行流跳转到该方法在内存中的入口地址,执行完毕后,再把返回值包装成一个 Object 扔给你。

4. 总结公式

Method.invoke(Object obj, Object... args) 死死印在脑子里:

动作 参数 1 (obj) 参数 2 (args) 解释
执行实例方法 具体的实例对象(不能是 null) 方法需要的参数 告诉机器: 来执行,参数是什么。
执行静态方法 null 方法需要的参数 告诉机器:不需要对象,直接用类执行,参数是什么。

通过这个 Programmer 的例子,有没有感觉 invoke 其实就是一个"脱离了具体对象、独立存在的动作执行器"?

理解了公开方法的调用后,想不想看看怎么用反射结合 invoke 强行破解并调用类里面被 private 藏起来的私有方法?

相关推荐
Pomelo_刘金1 分钟前
Rust 1.94.1 版本修复解析
后端·rust·编程语言
浪客川7 分钟前
【百例RUST - 015】闭包
开发语言·后端·rust
计算机安禾21 分钟前
【Linux从入门到精通】第9篇:用户与权限管理(下)——数字法与粘滞位
linux·服务器·人工智能·面试·知识图谱
楼田莉子25 分钟前
仿muduo的高并发服务器——前置知识讲解和时间轮模块
服务器·开发语言·c++·后端·学习
小江的记录本26 分钟前
【分布式】分布式核心组件——分布式限流:固定窗口、滑动窗口、漏桶、令牌桶算法,网关层/服务层限流实现
java·分布式·后端·python·算法·安全·面试
Hanson,27 分钟前
SpringBoot前后端分离框架中,在请求头加入签名
java·spring boot·后端
九转成圣29 分钟前
Spring Boot 导出 Excel 最佳实践:从 POI 函数式封装到 EasyExcel 的“降维打击”
spring boot·后端·excel
liyi_hz200830 分钟前
O2OA(翱途) V10 升级说明(三)数据中心:精准查询·严谨权限·优质视图
后端·java-ee·开源软件
刀法如飞38 分钟前
一款基于 NestJS 的 DDD 脚手架,开箱即用
javascript·后端·架构
鹏程十八少40 分钟前
3. 2026金三银四 Android 背完这 23 道题,Android 线程面试横着走
android·面试·前端框架