反射的初步理解

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


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

相关推荐
AI人工智能+电脑小能手5 小时前
【大白话说Java面试题 第87题】【Mysql篇】第17题:分布式事务的实现原理?
java·数据库·分布式·mysql·面试
红尘散仙5 小时前
我把终端小说阅读器接上了 AI Agent:TRNovel 现在能用 skill 生成书源了
人工智能·后端·rust
卷毛的技术笔记6 小时前
告别硬编码!Spring AI Alibaba 实现 AI Agent 智能工具调用(Tool Calling)
java·人工智能·后端·python·spring·ai编程
会编程的土豆7 小时前
Go 语言反射(Reflection)详解
开发语言·后端·golang
Cosolar7 小时前
从零写一个 Attention Is All You Need
人工智能·面试·架构
喵个咪7 小时前
GoWind Toolkit Go后端代码生成 完整全流程实战
后端·go·orm
basketball6167 小时前
Go 语言从入门到进阶:4. 数组和MAP使用方法总结
开发语言·后端·golang
qq_2518364577 小时前
SpringBoot+Vue 共享电池柜管理系统 完整实现 前后端分离项目实战 完整代码
vue.js·spring boot·后端
zhangxingchao8 小时前
AI 大模型核心六:量化、Workflow 与 Agent、多轮 RAG
前端·人工智能·后端
IT_陈寒9 小时前
Vite打包时遇到的坑,原来问题出在这里
前端·人工智能·后端