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

