詳解Java的回調(diào)機(jī)制
模塊之間總是存在這一定的接口,從調(diào)用方式上看,可以分為三類:同步調(diào)用、回調(diào)和異步調(diào)用。下面著重詳解回調(diào)機(jī)制。
1. 概述
Java 中的回調(diào)機(jī)制是一個(gè)比較常見的機(jī)制,只是有可能在你的程序中使用得比較少,在一些大型的框架中回調(diào)機(jī)制隨處可見。本文就通過一些具體的實(shí)例,慢慢走近 Java 的回調(diào)機(jī)制。
2.回調(diào)
所謂回調(diào):就是A類中調(diào)用B類中的某個(gè)方法C,然后B類中反過來調(diào)用A類中的方法D,D這個(gè)方法就叫回調(diào)方法。實(shí)際在使用的時(shí)候,也會有不同的回調(diào)形式,比如下面的這幾種。
2.1 同步回調(diào)
這里我假設(shè)這樣的一種情況。
A 公司的總監(jiān) B 跟他的下屬(項(xiàng)目經(jīng)理 C)說要做一個(gè)調(diào)研,不過不用 C 自己親力親為??梢宰尳?jīng)理 C 去安排他下面的程序員 D 去完成。經(jīng)理 C 找到了程序員 D,并告訴他,現(xiàn)在要完成一個(gè)調(diào)研任務(wù)。并且把調(diào)研的結(jié)果告訴經(jīng)理 C。如果有問題,還是要繼續(xù)的。 因?yàn)檫@里是 C 讓 D 去做一件事情,之后 D 還是要將結(jié)果與 C 進(jìn)行溝通。這樣就是回調(diào)的模型了。下面是一般回調(diào)的類圖:
首先我們要有一個(gè)回調(diào)的接口 CallbackInterface
CallbackInterface.java
public interface CallbackInterface { public boolean check(int result); }
背景里,程序員 D 是要將結(jié)果與項(xiàng)目經(jīng)理 C 進(jìn)行溝通的,所以這里項(xiàng)目經(jīng)理需要實(shí)現(xiàn)上面的回調(diào)接口:
Manager.java
public class Manager implements CallbackInterface { private Programmer programmer = null; public Manager(Programmer _programmer) { this.programmer = _programmer; } /** * 用于 Boss 下達(dá)的委托 */ public void entrust() { arrange(); } // 進(jìn)行安排下屬進(jìn)行 study 工作 private void arrange() { System.out.println("Manager 正在為 Programmer 安排工作"); programmer.study(Manager.this); System.out.println("為 Programmer 安排工作已經(jīng)完成,Manager 做其他的事情去了。"); } @Override public boolean check(int result) { if (result == 5) { return true; } return false; } }
對于程序員 D 來說他需要持有一個(gè)經(jīng)理 C 的引用,以便與他溝通。不過,這里是總監(jiān) B 讓 經(jīng)理 C 去安排的任務(wù)。也就是說這里也可以讓其他的經(jīng)理,比如說經(jīng)理 B1, B2等等。因?yàn)榻?jīng)理都實(shí)現(xiàn)了回調(diào)的接口,所以這里就可以直接讓程序員 D 持有這個(gè)接口就可以了。如下:
Programmer.java
public class Programmer { public void study(CallbackInterface callback) { int result = 0; do { result++; System.out.println("第 " + result + " 次研究的結(jié)果"); } while (!callback.check(result)); System.out.println("調(diào)研任務(wù)結(jié)束"); } }
對于總監(jiān)來說就更簡單明了了,因?yàn)檫@相當(dāng)于一個(gè) Client 測試:
Boss.java
public class Boss { public static void main(String[] args) { Manager manager = new Manager(new Programmer()); manager.entrust(); } }
運(yùn)行結(jié)果:
Manager 正在為 Programmer 安排工作
第 1 次研究的結(jié)果
第 2 次研究的結(jié)果
第 3 次研究的結(jié)果
第 4 次研究的結(jié)果
第 5 次研究的結(jié)果
調(diào)研任務(wù)結(jié)束
為 Programmer 安排工作已經(jīng)完成,Manager 做其他的事情去了。
2.2 異步回調(diào)
還是上面的例子,你的項(xiàng)目經(jīng)理不可能要一直等你調(diào)研的結(jié)果。而是把這個(gè)任務(wù)交給你之后,他就不管了,他做他的,你做你的。所以,這里需要對回調(diào)的函數(shù)進(jìn)行異步處理。
所以,這里我們需要修改 Programmer 類的代碼,修改如下:
Programmer.java
public class Programmer { public Programmer() { } public void study(CallbackInterface callback) { new StudyThread(callback).start(); } // --------------------------- Programmer 正在做的工作 --------------------------- class StudyThread extends Thread { CallbackInterface callback = null; public StudyThread(CallbackInterface _callback) { callback = _callback; } @Override public void run() { int result = 0; do { result++; System.out.println("第 " + result + " 次研究的結(jié)果"); } while (!callback.check(result)); System.out.println("調(diào)研任務(wù)結(jié)束"); } } }
運(yùn)行結(jié)果:
Manager 正在為 Programmer 安排工作
為 Programmer 安排工作已經(jīng)完成,Manager 做其他的事情去了。
第 1 次研究的結(jié)果
第 2 次研究的結(jié)果
第 3 次研究的結(jié)果
第 4 次研究的結(jié)果
第 5 次研究的結(jié)果
調(diào)研任務(wù)結(jié)束
2.3 閉包與回調(diào)
閉包(closure)是一個(gè)可調(diào)用的對象,它記錄了一些信息,這些信息來自于創(chuàng)建它的作用域。
2.3.1 普通調(diào)用
首先,我們可以看看在正常情況下的調(diào)用是怎么進(jìn)行的。
Incrementable.java
interface Incrementable { void increment(); }
這是一個(gè)普通的接口(在普通調(diào)用里只是普通接口,在回調(diào)中就是回調(diào)接口,這一點(diǎn)應(yīng)該很好理解吧)。
Callee1.java
class Callee1 implements Incrementable { private int i = 0; @Override public void increment() { i++; System.out.println(i); } }
Callbacks.java
public class Callbacks { public static void main(String[] args) { Callee1 callee1 = new Callee1(); callee1.increment(); } }
Callbacks 是一個(gè)測試客戶端類,沒啥好說的,直接看上面的代碼。
2.3.2 回調(diào)初試
上面的普通調(diào)用也沒啥好說的,因?yàn)檫@對于一個(gè)正常的 Java 程序員來說都應(yīng)該是想都不用想就可以搞定的事情。
現(xiàn)在如果要構(gòu)成回調(diào),那么對于程序的結(jié)構(gòu)或是邏輯的思維上都不可能只有一個(gè)被調(diào)用者(被回調(diào)的對象 Callee1),還需要一個(gè)調(diào)用者對象。調(diào)用者可以像下面這樣來編寫:
Caller.java
class Caller { private Incrementable callbackReference; public Caller(Incrementable _callbackReference) { callbackReference = _callbackReference; } void go() { callbackReference.increment(); } }
這里 Caller 持有一個(gè)回調(diào)接口的引用 callbackReference,就像在上面說到的程序員需要持有一個(gè)項(xiàng)目經(jīng)理的引用,這樣就可以通過這個(gè)引用來與項(xiàng)目經(jīng)理溝通。這里的 callbackReference 也正是起到了這個(gè)作用。
現(xiàn)在我們來看看測試類的編寫:
Callbacks.java
public class Callbacks { public static void main(String[] args) { Callee1 callee1 = new Callee1(); Caller caller1 = new Caller(callee1); caller1.go(); } }
對于到目前為止的程序代碼,完全可以對比上面項(xiàng)目經(jīng)理安排程序員調(diào)研技術(shù)難題的代碼。有異曲同工之妙。
2.3.3 閉包回調(diào)
相比于正常的回調(diào),閉包回調(diào)的核心自然是在于閉包,也就是對作用域的控制。
現(xiàn)在假設(shè)有一個(gè)用戶(其他程序員)自定義了一個(gè) MyInCrement 類,同時(shí)包含了一個(gè) increment 的方法。如下:
class MyInCrement { public void increment() { System.out.println("MyCrement.increment"); } static void f(MyInCrement increment) { increment.increment(); } }
另外有一個(gè)類 Callee2 繼承自上面這個(gè)類:
class Callee2 extends MyInCrement { private int i = 0; public void increment() { super.increment(); i++; System.out.println(i); } }
顯而易見這里如果要調(diào)用 increment() 方法,就變成了一般的函數(shù)調(diào)用了。所以這里我們需要修改上面的 Callee2 類,修改的目標(biāo)就是讓 Callee2 類可以兼容 MyInCrement 類的 increment() 方法和 Incrementable 的 increment() 方法。修改后:
class Callee2 extends MyInCrement { private int i = 0; public void increment() { super.increment(); i++; System.out.println(i); } private class Closure implements Incrementable { @Override public void increment() { Callee2.this.increment(); } } Incrementable getCallbackReference() { return new Closure(); } }
注意,這里的 Closure 類是一個(gè)私有的類,這是一個(gè)閉包的要素。因?yàn)?Closure 類是私有的,那么就要有一個(gè)對外開放的接口,用來對 Closure 對象的操作,這里就是上面的 getCallbackReference() 方法。 Caller 類則沒有改變。
對于測試客戶端就直接看代碼吧:
public class Callbacks { public static void main(String[] args) { Callee2 callee2 = new Callee2(); Caller caller2 = new Caller(callee2.getCallbackReference()); caller2.go(); } }
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- java調(diào)用回調(diào)機(jī)制詳解
- 通過簡易例子講解Java回調(diào)機(jī)制
- Java的回調(diào)機(jī)制實(shí)例詳解
- 深入了解Java接口回調(diào)機(jī)制
- 詳解java 三種調(diào)用機(jī)制(同步、回調(diào)、異步)
- java 回調(diào)機(jī)制的實(shí)例詳解
- java回調(diào)機(jī)制實(shí)例詳解
- Java回調(diào)機(jī)制解讀
- Java 異步回調(diào)機(jī)制實(shí)例分析
- Java 回調(diào)機(jī)制(CallBack) 詳解及實(shí)例代碼
- 兩個(gè)例子了解java中的回調(diào)機(jī)制
相關(guān)文章
詳解Spring Cloud負(fù)載均衡重要組件Ribbon中重要類的用法
本篇文章主要介紹了詳解Spring Cloud負(fù)載均衡重要組件Ribbon中重要類的用法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03SpringBoot+MyBatis+AOP實(shí)現(xiàn)讀寫分離的示例代碼
高并發(fā)這個(gè)階段,肯定是需要做MySQL讀寫分離的。本文主要介紹了SpringBoot+MyBatis+AOP實(shí)現(xiàn)讀寫分離的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11Springboot獲取jar包中resources資源目錄下的文件
今天在項(xiàng)目中遇到一個(gè)業(yè)務(wù)場景,需要用到resources資源目錄下的文件,本文主要介紹了Springboot獲取jar包中resources資源目錄下的文件,感興趣的可以了解一下2023-12-12Java程序結(jié)構(gòu)與常量變量難點(diǎn)解析
JAVA的基本結(jié)構(gòu)就是順序結(jié)構(gòu),除非特別指明,否則就按照順序一句一句執(zhí)行順序結(jié)構(gòu)是最簡單的算法結(jié)構(gòu),語句與語句之間,框與框之間是按從上到下的順序進(jìn)行的,它是由若干個(gè)依次執(zhí)行的處理步驟組成的,它是任何一個(gè)算法都離不開的一種基本算法結(jié)構(gòu)2021-10-10SpringBoot整合Spring Security構(gòu)建安全的Web應(yīng)用
pring Security是一個(gè)強(qiáng)大的身份驗(yàn)證和訪問控制框架,本文主要介紹了SpringBoot整合Spring Security構(gòu)建安全的Web應(yīng)用,具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01