反射的初步理解

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


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

相关推荐
swipe19 小时前
纯函数、柯里化与函数组合:从原理到源码,构建更可维护的前端代码体系
前端·javascript·面试
下次一定x19 小时前
深度解析 Kratos 客户端服务发现与负载均衡:从 Dial 入口到 gRPC 全链路落地(上篇)
后端·go
kevinzeng19 小时前
Spring 核心知识点:EnvironmentAware 接口详解
后端
xyy12319 小时前
C# / ASP.NET Core 依赖注入 (DI) 核心知识点
后端
Lee川19 小时前
JavaScript 中的 `this` 与变量查找:一场关于“身份”与“作用域”的深度博弈
前端·javascript·面试
yuhaiqiang20 小时前
为什么我建议你不要只问一个AI?🤫偷偷学会“群发”,答案准到离谱!
人工智能·后端·ai编程
双向3321 小时前
AR 眼镜拯救社恐:我用 Kotlin 写了个拜年提词器
后端
吾日三省Java21 小时前
Spring Cloud架构下的日志追踪:传统MDC vs 王炸SkyWalking
java·后端·架构
想打游戏的程序猿21 小时前
服务端用AI写前端:隐患、困境与思考
后端