一文搞懂「回调」:从前端箭头函数到 Java Lambda 与线程模型

很多初学者(甚至工作一两年的人)都会卡在几个问题上:

  • 什么是回调
  • 为什么前端到处是 () => {}
  • Java 的 () -> {}(Lambda)到底在干嘛
  • Runnable 是什么?为什么线程 start() 里看不到 run()
  • 排序、遍历为什么也要传函数?

这些问题表面看是语法差异,本质其实是同一个思想

这篇文章,我试着用一条主线把它们全部串起来。


一、先给结论:什么是回调?

回调 = 把“将来要执行的代码”,先交给别人,让别人合适的时候再来调用你

关键词只有一个:不是你现在调用,是别人之后调用


二、最生活化的理解(非常重要)

想象你点外卖,对骑手说一句话:

“到了给我打电话”

你做了三件事:

  1. 把事交给了别人(骑手)
  2. 告诉他 到时候要干什么
  3. 你自己不用等、也不用管

📌
这个“到了给我打电话”,本质就是回调


三、前端:为什么 () => {} 到处都是回调?

1️⃣ 点击事件(最典型)

button.onclick = () => {
  console.log("我被点了");
};

这里发生了什么?

  • 你没有调用这个函数
  • 你只是把它交给浏览器
  • 将来用户点击时,浏览器来调用你

👉 这是 100% 的回调


2️⃣ 定时器

setTimeout(() => {
  console.log("3 秒后执行");
}, 3000);

人话翻译:

“你帮我计时,时间到了再执行我给你的代码”

你不知道 3 秒什么时候结束,
所以只能用回调告诉它结束后怎么办


3️⃣ 数组遍历 / 排序(容易被忽略)

[3, 1, 2].sort((a, b) => a - b);

你其实是在对数组说:

“我不管你怎么排
两个数谁在前,你用这段规则来判断”

📌
这个 (a, b) => a - b不是你在调用
sort 内部在反复调用 —— 所以它也是回调。


四、JS 箭头函数 () => {} 本身是不是回调?

不是。

() => {} 只是一个“函数写法”

当它被“当参数传出去,将来再执行”时,才叫回调。

对比一下就清楚了:

// 普通函数调用(不是回调)
const f = () => console.log("hi");
f();

// 回调
setTimeout(() => console.log("hi"), 1000);

👉 区别只在一句话:谁来调用?什么时候调用?


五、Java:Lambda () -> {} 是什么?

Java 的 Lambda,本质和 JS 箭头函数是一样的:

把一段可执行逻辑,当成一个值传来传去

对照一下你就明白了

JSJava
() => {}() -> {}
回调函数Lambda
浏览器/事件循环调用线程/框架调用

六、Runnable 是什么?为什么线程要它?

先看 Runnable 的真身:

public interface Runnable {
    void run();
}

就这一行。

用一句大白话解释:

Runnable = 一份“任务说明书”
上面只写一件事:run() 里要干什么

不执行
它只是描述“要执行的逻辑”。


七、线程为什么是 start(),而不是 run()

这是很多人第一次看源码时的巨大疑惑。

你甚至已经发现了这一点:
Thread.start() 的源码里根本没调用 run()

原因只有一句话:

run() 必须在“新线程”里执行,而 start() 是在“当前线程”里被调用的

真正的执行链路是:

main 线程
  ↓
Thread.start()
  ↓
native start0()   (JVM / OS 创建新线程)
  ↓
新线程入口
  ↓
Thread.run()
  ↓
Runnable.run()

📌
run()新线程启动后,被 JVM 回调执行的


八、为什么说“线程模型本身就是回调模型”?

你写的代码是这样的:

new Thread(() -> {
    System.out.println("do work");
}).start();

你实际上是在对 JVM 说:

“线程什么时候真正跑起来,我不管
跑起来以后,请执行我给你的这段代码”

这和前端的:

setTimeout(() => {}, 1000);

思想一模一样


九、排序、遍历、线程:为什么都要用回调?

因为它们有一个共同点:

控制权不在你手里

  • 排序:算法在库里
  • 遍历:循环在框架里
  • 线程:执行时机在 JVM / OS
  • 点击:事件在浏览器

你只能告诉它一句话:

“到你合适的时候,用这段逻辑处理”

👉 这就是回调存在的根本原因。


十、一句话把整篇文章串起来

回调不是“高级技巧”,而是一种“控制权转移”的编程方式

  • 你不决定“什么时候执行”
  • 你只提供“怎么执行”
  • 真正执行的那一刻,由别人来回头调用你

十一、你现在如果能说出这几句话,说明真的懂了

  • () => {} / () -> {} 只是函数写法
  • 被传出去、将来被别人调用 → 才叫回调
  • Runnable 不是线程,是线程要执行的内容
  • start() 不直接调用 run(),是 JVM 在新线程里回调它
  • 排序、遍历、线程、事件,本质都是同一套思想

更多推荐