反射的初步理解

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


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 藏起来的私有方法?

相关推荐
菜鸟谢1 小时前
Rust 智能指针完整详解
后端
GuWenyue1 小时前
排序效率低?5分钟吃透快速排序,性能飙升至O(nlogn)
前端·javascript·面试
菜鸟谢1 小时前
Rust 函数完整知识点详解
后端
爱勇宝1 小时前
淡泊名利之前,先承认我们都很焦虑
前端·后端·程序员
菜鸟谢1 小时前
Rust 闭包(Closure)完整详解
后端
ServBay1 小时前
如何利用本地技术栈构建 0 成本 AI SaaS 雏形
后端·aigc·ai编程
菜鸟谢1 小时前
Rust 集合 + 迭代器完整详解
后端
杨利杰YJlio2 小时前
Codex桌面客户端上手:项目、插件与自动化实战
前端·后端
ricardo19732 小时前
React 渲染优化:memo / useMemo / useCallback 的正确姿势与并发模式实战
前端·面试
常铭2 小时前
【Java基础】01-HashMap的底层原理
后端·面试