一文徹底搞懂Java中的回調(diào)函數(shù)
在學(xué)習(xí)代理設(shè)計模式中提到了“回調(diào)”這個東東,一下子把我打蒙了。于是決心花點小時間徹底理解java里的回調(diào)是什么。
是什么
定義
回調(diào)(Callback)是 Java 中最基礎(chǔ)、最核心的編程設(shè)計思想,本質(zhì)是「方法作為參數(shù)傳遞 + 控制權(quán)反轉(zhuǎn)」的實現(xiàn)。回調(diào)邏輯的執(zhí)行時機,由被調(diào)用方?jīng)Q定,而非調(diào)用方(控制權(quán)反轉(zhuǎn))
回調(diào) = 調(diào)用方把「自己的邏輯(方法)」交給被調(diào)用方 → 被調(diào)用方在「指定時機」主動執(zhí)行該邏輯。
好比點外賣時(場景)我把我的電話號碼(對象)交給店鋪(被調(diào)用者),店鋪會在我的外賣送到時(指定時機)給我打電話(對象可以調(diào)用的方法)。
成員:
心成員 1:「調(diào)用方(Caller)」
? 核心職責(zé):定義回調(diào)邏輯(或持有已有類的回調(diào)方法) + 將回調(diào)載體(接口對象 / 普通類對象)傳遞給被調(diào)用方;
? 核心特征:主動發(fā)起交互,把自己的邏輯交付出去,不控制回調(diào)執(zhí)行時機。
核心成員 2:「被調(diào)用方(Callee)」
? 核心職責(zé):接收回調(diào)載體 + 執(zhí)行自身核心業(yè)務(wù) + 在指定時機主動調(diào)用回調(diào)載體中的方法;
? 核心特征:擁有「回調(diào)觸發(fā)權(quán)」,決定回調(diào)邏輯什么時候執(zhí)行,是回調(diào)的主導(dǎo)方。
核心成員 3:「回調(diào)體(Callback Body)」
? 核心定義:封裝了回調(diào)邏輯的載體(可以是「接口實現(xiàn)類對象」、也可以是「已有方法的普通類對象」),是調(diào)用方與被調(diào)用方的「通信橋梁」;
? 核心特征:內(nèi)部包含具體的「回調(diào)方法」,供被調(diào)用方主動觸發(fā)。
實例
同步回調(diào)(對應(yīng)的異步會回調(diào)就是在被調(diào)用方內(nèi)部開個獨立線程)
// ========== 步驟1:定義回調(diào)接口(約定回調(diào)方法規(guī)范,核心) ==========
interface MyCallback {
// 回調(diào)方法:被調(diào)用方在「任務(wù)完成后」主動調(diào)用
void onTaskFinish(String result);
}
// ========== 步驟2:被調(diào)用方:接收回調(diào)對象,執(zhí)行核心任務(wù)后觸發(fā)回調(diào) ==========
class CallbackTaskService {
// 核心方法:入?yún)椤富卣{(diào)接口對象」,接收調(diào)用方的邏輯
public void doTask(MyCallback callback) {
System.out.println("【被調(diào)用方】開始執(zhí)行耗時核心任務(wù)...");
// 模擬核心任務(wù)執(zhí)行(如數(shù)據(jù)庫查詢、網(wǎng)絡(luò)請求)
String taskResult = "核心任務(wù)執(zhí)行成功,返回結(jié)果";
// 關(guān)鍵:被調(diào)用方主動觸發(fā)回調(diào)(時機由被調(diào)用方?jīng)Q定)
callback.onTaskFinish(taskResult);
}
}
// ========== 步驟3:調(diào)用方:實現(xiàn)回調(diào)接口,傳遞回調(diào)對象 ==========
public class CallbackCallDemo {
public static void main(String[] args) {
CallbackTaskService service = new CallbackTaskService();
// 調(diào)用方通過「匿名內(nèi)部類」實現(xiàn)回調(diào)接口
MyCallback callback = new MyCallback() {
@Override
public void onTaskFinish(String result) {
// 調(diào)用方自定義的回調(diào)邏輯:處理被調(diào)用方返回的結(jié)果
System.out.println("【調(diào)用方-回調(diào)邏輯】收到被調(diào)用方結(jié)果:" + result);
}
};
// 傳遞回調(diào)對象給被調(diào)用方
service.doTask(callback);
// 核心優(yōu)勢:調(diào)用方無需等待,可立即執(zhí)行自身其他邏輯
System.out.println("【調(diào)用方】發(fā)起任務(wù)后,直接執(zhí)行自身后續(xù)邏輯,無需等待任務(wù)完成");
}
}注意,這里不是必須得有個接口什么的,這不是一種固定的代碼寫法而是一種設(shè)計模式。只是使用接口可以實現(xiàn)強解耦,統(tǒng)一規(guī)范,是最佳實踐。在實例代碼的基礎(chǔ)上還可以用lambda簡化等等。
常見應(yīng)用
像代理模式中就有用到,不過里面層層包裝很難看清怎么用的,這里只講講線程 / 線程池回調(diào)。
// 回調(diào)體:Runnable是回調(diào)接口,匿名內(nèi)部類實現(xiàn)的是「回調(diào)對象」
Runnable callbackObj = new Runnable() {
@Override
public void run() {
// ?? 回調(diào)方法:存放「用戶自定義的業(yè)務(wù)邏輯」(擴展邏輯)
System.out.println("【線程回調(diào)】執(zhí)行線程內(nèi)業(yè)務(wù)邏輯");
}
};
// 被調(diào)用方:Thread類 + JVM(核心邏輯執(zhí)行者,掌握回調(diào)觸發(fā)權(quán))
Thread thread = new Thread(callbackObj);
// 調(diào)用方:我們的主線程代碼(傳遞回調(diào)體、發(fā)起調(diào)用,不控制觸發(fā)時機)
thread.start(); 執(zhí)行鏈路
1. 主線程調(diào)用 thread.start() → 向JVM發(fā)起「線程啟動請求」,線程進入「就緒狀態(tài)」;
2. JVM線程調(diào)度器分配CPU資源 → 線程從「就緒」轉(zhuǎn)為「運行狀態(tài)」;
3. JVM底層主動調(diào)用 Thread類中的run()方法;
4. Thread類的run()方法內(nèi)部,會調(diào)用「傳入的Runnable對象」的run()方法;
5. 最終觸發(fā):我們自定義的回調(diào)邏輯(Runnable匿名內(nèi)部類的run())執(zhí)行;
6. 業(yè)務(wù)邏輯執(zhí)行完畢 → 線程進入「終止?fàn)顟B(tài)」,回調(diào)完成。
常見應(yīng)用場景
| 應(yīng)用領(lǐng)域 | 具體場景 | 核心回調(diào)接口 / 方法 | 觸發(fā)時機 |
|---|---|---|---|
| JDK 原生 | 線程 / 線程池 | Runnable.run()/Callable.call() | 線程啟動后 |
| JDK 原生 | 異步回調(diào) | CompletableFuture.thenAccept() | 異步任務(wù)完成后 |
| JDK 原生 | 集合遍歷 | Collection.forEach() | 遍歷每個元素時 |
| Spring 生態(tài) | Bean 生命周期 | InitializingBean.afterPropertiesSet() | Bean 初始化完成后 |
| Spring 生態(tài) | 事件監(jiān)聽 | ApplicationListener.onApplicationEvent() | 發(fā)布事件時 |
| Spring 生態(tài) | MVC 攔截器 | HandlerInterceptor.preHandle() | 請求處理前后 |
| 動態(tài)代理 | CGLIB/JDK 代理 | MethodInterceptor.intercept()/InvocationHandler.invoke() | 調(diào)用代理方法時 |
| MyBatis | 類型轉(zhuǎn)換 | TypeHandler.setNonNullParameter() | 參數(shù)設(shè)置 / 結(jié)果映射時 |
| Netty | NIO 事件 | ChannelHandler.channelRead() | IO 事件觸發(fā)時 |
| 定時任務(wù) | 定時器 | TimerTask.run() | 定時時間到達時 |
為什么
其實跟大多數(shù)設(shè)計模式一樣,還是「傳遞邏輯、移交控制、分離職責(zé)」這些優(yōu)點,幫助我們寫出邏輯性更強更簡約的代碼。
不過這里的什么解決代碼緊耦合,設(shè)計模式里的裝飾器模式,觀察者模式等等都能做到。代碼量太低的我就很難理解里面的精妙之處,特別之處了,這里更多的是記住“這種場景”來選擇使用。
總結(jié)
到此這篇關(guān)于Java中回調(diào)函數(shù)的文章就介紹到這了,更多相關(guān)Java回調(diào)函數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用IntelliJ IDEA調(diào)式Stream流的方法步驟
本文主要介紹了使用IntelliJ IDEA調(diào)式Stream流的方法步驟,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-05-05
Spring Bean初始化及@PostConstruc執(zhí)行順序示例詳解
本文給大家介紹Spring Bean初始化及@PostConstruc執(zhí)行順序,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2025-07-07
關(guān)于Springboot | @RequestBody 接收到的參數(shù)對象屬性為空的問題
這篇文章主要介紹了關(guān)于Springboot | @RequestBody 接收到的參數(shù)對象屬性為空的問題,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03

