Java中的回調(diào)機(jī)制使用方式
什么是回調(diào)機(jī)制
回調(diào)機(jī)制(Callback)是一種常見的編程模式,它允許一個類或?qū)ο笤谔囟ㄊ录l(fā)生時通知另一個類或?qū)ο?。簡單來說,就是"A調(diào)用B,B在執(zhí)行完成后又調(diào)用A"的過程。
回調(diào)的核心思想是反向調(diào)用,這與傳統(tǒng)的正向調(diào)用(直接調(diào)用方法)不同。回調(diào)機(jī)制在事件處理、異步編程、框架設(shè)計中有著廣泛應(yīng)用。
在 Java 中,回調(diào)最常見的實現(xiàn)方式是:
- 定義一個回調(diào)接口(Callback Interface)。
- 調(diào)用方持有該接口的引用,并將其傳遞給被調(diào)用方。
- 被調(diào)用方在合適的時機(jī)反過來調(diào)用接口方法,把結(jié)果或事件通知給調(diào)用方。
回調(diào)的基本實現(xiàn)方式
1. 基于接口的回調(diào)
這是Java中最常用的回調(diào)實現(xiàn)方式。我們定義一個回調(diào)接口,然后讓調(diào)用方實現(xiàn)這個接口,最后將實現(xiàn)類的實例傳遞給被調(diào)用方。
示例:按鈕點擊事件
// 定義回調(diào)接口
interface ClickListener {
void onClick();
}
// 按鈕類
class Button {
private ClickListener listener;
// 設(shè)置回調(diào)監(jiān)聽器
public void setClickListener(ClickListener listener) {
this.listener = listener;
}
// 模擬按鈕被點擊
public void click() {
System.out.println("按鈕被點擊了");
if (listener != null) {
listener.onClick(); // 觸發(fā)回調(diào)
}
}
}
public class InterfaceCallbackDemo {
public static void main(String[] args) {
Button button = new Button();
// 設(shè)置回調(diào)實現(xiàn)
button.setClickListener(new ClickListener() {
@Override
public void onClick() {
System.out.println("回調(diào)執(zhí)行:按鈕點擊事件處理");
System.out.println("回調(diào)執(zhí)行了~");
}
});
button.click(); // 觸發(fā)點擊事件
}
}
說明:
- 定義
ClickListener回調(diào)接口 Button類持有接口引用,并在適當(dāng)時機(jī)調(diào)用接口方法- 主程序通過匿名類實現(xiàn)接口,完成回調(diào)設(shè)置
- 當(dāng)
click()方法被調(diào)用時,會觸發(fā)回調(diào)
執(zhí)行結(jié)果:
按鈕被點擊了 回調(diào)執(zhí)行:按鈕點擊事件處理 回調(diào)執(zhí)行了~
2. 抽象類回調(diào)
示例:任務(wù)處理器
// 定義抽象回調(diào)類
abstract class TaskHandler {
// 抽象回調(diào)方法
public abstract void onComplete(String result);
// 可以有具體實現(xiàn)方法
public void onStart() {
System.out.println("任務(wù)開始處理");
}
}
// 任務(wù)執(zhí)行類
class TaskExecutor {
public void execute(TaskHandler handler) {
handler.onStart();
// 模擬任務(wù)執(zhí)行
try {
Thread.sleep(1000);
handler.onComplete("任務(wù)完成");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
public class AbstractClassCallbackDemo {
public static void main(String[] args) {
TaskExecutor executor = new TaskExecutor();
// 使用匿名類實現(xiàn)抽象回調(diào)類
executor.execute(new TaskHandler() {
@Override
public void onComplete(String result) {
System.out.println("回調(diào)結(jié)果: " + result);
}
});
}
}
說明:
- 定義
TaskHandler抽象類,包含抽象回調(diào)方法 - 抽象類可以包含具體實現(xiàn)方法(如
onStart()) TaskExecutor接收抽象類作為參數(shù),調(diào)用其方法- 主程序通過匿名類實現(xiàn)抽象類,完成回調(diào)設(shè)置
執(zhí)行結(jié)果:
任務(wù)開始處理 回調(diào)結(jié)果: 任務(wù)完成
3. 函數(shù)式接口與Lambda表達(dá)式(Java 8+)
示例:簡單計算器
import java.util.function.BiFunction;
public class LambdaCallbackDemo {
public static void main(String[] args) {
// 使用BiFunction作為回調(diào)接口
calculate(5, 3, (a, b) -> a + b); // 加法
calculate(5, 3, (a, b) -> a * b); // 乘法
// 更復(fù)雜的示例,數(shù)據(jù)處理回調(diào)
processData("Hello",
data -> {
String result = data + " World!";
System.out.println("處理結(jié)果: " + result);
return result;
},
error -> System.err.println("錯誤: " + error)
);
//
processData(null,
data -> data + " World!",
error -> System.err.println("捕獲到錯誤: " + error)
);
}
// 計算方法,接收操作回調(diào)
public static void calculate(int a, int b, BiFunction<Integer, Integer, Integer> operation) {
int result = operation.apply(a, b);
System.out.println("計算結(jié)果: " + result);
}
// 數(shù)據(jù)處理方法,接收成功和失敗回調(diào)
public static void processData(String input,
java.util.function.Function<String, String> successHandler,
java.util.function.Consumer<Exception> errorHandler) {
try {
if (input == null) {
throw new IllegalArgumentException("輸入不能為null");
}
String result = successHandler.apply(input);
} catch (Exception e) {
errorHandler.accept(e);
}
}
}
說明:
- 使用Java內(nèi)置的
BiFunction函數(shù)式接口作為回調(diào) Lambda表達(dá)式簡化了回調(diào)的實現(xiàn)calculate方法接收操作邏輯作為參數(shù)processData方法用了更復(fù)雜的回調(diào)場景,包含成功和錯誤處理
執(zhí)行結(jié)果:
計算結(jié)果: 8 計算結(jié)果: 15 處理結(jié)果: Hello World! 捕獲到錯誤: java.lang.IllegalArgumentException: 輸入不能為null
回調(diào)的同步與異步特性
回調(diào)本身只是一種編程模式,它既可以是同步的也可以是異步的,這取決于具體的實現(xiàn)方式
同步回調(diào)
- 特點:回調(diào)方法在調(diào)用者方法返回前執(zhí)行
- 執(zhí)行流程:A調(diào)用B → B執(zhí)行 → B調(diào)用A的回調(diào)方法 → B返回 → A繼續(xù)執(zhí)行
如:集合排序時傳入的Comparator
同步回調(diào)實現(xiàn)示例
// 同步回調(diào)接口
interface SyncCallback {
void onComplete(String result);
}
class SyncProcessor {
public void process(String input, SyncCallback callback) {
System.out.println("處理線程: " + Thread.currentThread().getName());
// 同步處理
String result = input.toUpperCase();
// 同步調(diào)用回調(diào)
callback.onComplete(result);
}
}
public class SyncCallbackDemo {
public static void main(String[] args) {
SyncProcessor processor = new SyncProcessor();
System.out.println("主線程: " + Thread.currentThread().getName());
processor.process("hello", result -> {
System.out.println("回調(diào)線程: " + Thread.currentThread().getName());
System.out.println("同步結(jié)果: " + result);
});
System.out.println("主線程繼續(xù)執(zhí)行...");
}
}
執(zhí)行結(jié)果
主線程: main 處理線程: main 回調(diào)線程: main 同步結(jié)果: HELLO 主線程繼續(xù)執(zhí)行...
異步回調(diào)
- 特點:回調(diào)方法在另一個線程執(zhí)行
- 執(zhí)行流程:A調(diào)用B → B立即返回 → B啟動新線程執(zhí)行任務(wù) → 任務(wù)完成后在新線程調(diào)用A的回調(diào)方法
如:網(wǎng)絡(luò)請求的響應(yīng)回調(diào)
異步回調(diào)實現(xiàn)示例
// 異步回調(diào)接口
interface AsyncCallback {
void onComplete(String result);
}
class AsyncProcessor {
public void process(String input, AsyncCallback callback) {
System.out.println("調(diào)用線程: " + Thread.currentThread().getName());
// 啟動新線程異步處理
new Thread(() -> {
System.out.println("處理線程: " + Thread.currentThread().getName());
try {
Thread.sleep(1000); // 模擬耗時操作
String result = input.toUpperCase();
callback.onComplete(result);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
}
public class AsyncCallbackDemo {
public static void main(String[] args) {
AsyncProcessor processor = new AsyncProcessor();
System.out.println("主線程: " + Thread.currentThread().getName());
processor.process("hello", result -> {
System.out.println("回調(diào)線程: " + Thread.currentThread().getName());
System.out.println("異步結(jié)果: " + result);
});
System.out.println("主線程繼續(xù)執(zhí)行...");
// 防止主線程過早退出
try { Thread.sleep(1500); } catch (InterruptedException e) {}
}
}
執(zhí)行結(jié)果
主線程: main 調(diào)用線程: main 主線程繼續(xù)執(zhí)行... 處理線程: Thread-0 回調(diào)線程: Thread-0 異步結(jié)果: HELLO
回調(diào)的一些應(yīng)用實例
| 場景 | 回調(diào)接口 | 說明 |
|---|---|---|
| JDBC 驅(qū)動 | RowCallbackHandler | Spring JdbcTemplate 每查出一行就回調(diào)一次 |
| GUI 事件 | ActionListener | Swing/AWT 點擊按鈕觸發(fā) |
| Servlet 3.0 | AsyncListener | 異步 Servlet 完成/超時/錯誤時回調(diào) |
| Netty | ChannelFutureListener | IO 操作完成后回調(diào) |
| Spring 生命周期 | InitializingBean, DisposableBean | 容器啟動/銷毀時回調(diào) |
| Guava | ListenableFuture + Futures.addCallback | 早于 CompletableFuture 的回調(diào)方案 |
小結(jié)
回調(diào)的優(yōu)缺點
優(yōu)點:
- 解耦:回調(diào)可以將調(diào)用方和被調(diào)用方解耦
- 異步處理:非常適合處理異步操作和事件驅(qū)動編程
- 靈活性:可以在運(yùn)行時決定具體執(zhí)行什么操作
缺點:
- 回調(diào)地獄:多層嵌套回調(diào)會導(dǎo)致代碼難以閱讀和維護(hù)
- 異常處理復(fù)雜:在異步回調(diào)中處理異常比同步代碼更困難
- 調(diào)試?yán)щy:回調(diào)的執(zhí)行流程不如線性代碼直觀
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
基于Spring AOP @AspectJ進(jìn)階說明
這篇文章主要介紹了基于Spring AOP @AspectJ進(jìn)階說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-01-01
完美解決Spring Boot前端的Access-Control-Allow-Origin跨域問題
這篇文章主要介紹了完美解決Spring Boot前端的Access-Control-Allow-Origin跨域問題,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-05-05
SpringBoot事件發(fā)布與監(jiān)聽超詳細(xì)講解
今天去官網(wǎng)查看spring boot資料時,在特性中看見了系統(tǒng)的事件及監(jiān)聽章節(jié),所以下面這篇文章主要給大家介紹了關(guān)于SpringBoot事件發(fā)布和監(jiān)聽的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-11-11

