在正常写代码时,我们习惯了"对象.方法() "这种顺理成章的调用方式。但反射完全把这个逻辑反转了过来。
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") 时:
- JVM 内部会进行类型校验:检查
xiaoMing这个对象到底包不包含typeMethod这个方法的指令码。 - 检查你传的参数
"Java"的类型和数量对不对得上。 - 如果都对,JVM 会动态地把执行流跳转到该方法在内存中的入口地址,执行完毕后,再把返回值包装成一个
Object扔给你。
4. 总结公式
把 Method.invoke(Object obj, Object... args) 死死印在脑子里:
| 动作 | 参数 1 (obj) | 参数 2 (args) | 解释 |
|---|---|---|---|
| 执行实例方法 | 具体的实例对象(不能是 null) | 方法需要的参数 | 告诉机器:谁 来执行,参数是什么。 |
| 执行静态方法 | null |
方法需要的参数 | 告诉机器:不需要对象,直接用类执行,参数是什么。 |
通过这个 Programmer 的例子,有没有感觉 invoke 其实就是一个"脱离了具体对象、独立存在的动作执行器"?
理解了公开方法的调用后,想不想看看怎么用反射结合 invoke 强行破解并调用类里面被 private 藏起来的私有方法?