Java?回調(diào)callback舉例詳解
前言
回調(diào)的核心就是回調(diào)方將本身即this傳遞給調(diào)用方,這樣調(diào)用方就可以在調(diào)用完畢之后告訴回調(diào)方它想要知道的信息。
1、什么是回調(diào)
軟件模塊之間總是存在一定的接口,從調(diào)用方式上,可以把他們分為三類:同步調(diào)用、回調(diào)和異步調(diào)用。
(1)同步調(diào)用:
同步調(diào)用是最基本并且最簡單的一種調(diào)用方式,類A的方法a()調(diào)用類B的方法b(),一直等待b()方法執(zhí)行完畢,a()方法再繼續(xù)往下走。這種調(diào)用方式適用于方法b()執(zhí)行時間不長的情況,因為b()方法執(zhí)行時間一長或者直接阻塞的話,a()方法的余下代碼是無法執(zhí)行下去的,這樣會造成整個流程的阻塞。
(2)異步調(diào)用:
是一種類似消息或事件的機(jī)制,是為了解決同步調(diào)用可能出現(xiàn)阻塞,導(dǎo)致整個流程卡住而產(chǎn)生的一種調(diào)用方式。類A的方法a()通過新起線程的方式調(diào)用類B的方法b(),代碼接著直接往下執(zhí)行,這樣無論方法b()執(zhí)行時間多久,都不會阻塞方法a()的執(zhí)行。但是這種方式,由于方法a()不等待方法b()執(zhí)行完成,在方法a()需要方法b()執(zhí)行結(jié)果的情況下(視具體業(yè)務(wù)而定,有些業(yè)務(wù)比如啟動異步線程發(fā)個微信通知、刷新一個緩存這種就沒有必要),必須通過一定的方法對方法b()的執(zhí)行結(jié)果進(jìn)行監(jiān)聽。在Java中,可以使用Future+Callable的方式做到這一點(diǎn)。
(3)回調(diào):
最后是回調(diào),回調(diào)的思想是:
類A的a()方法調(diào)用了類B的b()方法類B的b方法執(zhí)行完畢主動調(diào)用類A的callback()方法
這樣一種調(diào)用方式組成了上圖,也就是一種雙向的調(diào)用方式
回調(diào)函數(shù)是一個函數(shù)或過程,不過它是一個由調(diào)用方自己實(shí)現(xiàn),供被調(diào)用方使用的特殊函數(shù)。
在面向?qū)ο蟮恼Z言中,回調(diào)則是通過接口或抽象類來實(shí)現(xiàn)的,我們把實(shí)現(xiàn)這種接口的類稱為回調(diào)類,回調(diào)類的對象稱為回調(diào)對象。
2、例子
開始之前。先想象一個場景:幼稚園的小朋友剛剛學(xué)習(xí)了10以內(nèi)的加法。
第一章.故事的緣起
幼師在黑板上寫一個式子 “1+1=”,由小明來填空由于已經(jīng)學(xué)習(xí)了10以內(nèi)的加法,小明同學(xué)可以完全靠自己來計算這個題目,模擬該過程的代碼如下:
public class Student { private String name=null; public Student(String name) { this.name=name; } public void setName(String name) { this.name=name; } private int calcADD(int a,int b) { return a+b; } public void fillBlank(int a,int b) { int result=calcADD(a,b); System.out.println(name+"心算:"+a+"+"+b+"="+result); } }
小明同學(xué)在填空(fillBlank)的時候,直接心算(clacADD)了一下,得出結(jié)果是2,并將結(jié)果寫在空格里。
測試代碼如下:
public class Test { public static void main(String[] args) { int a=1; int b=1; Student s=new Student("小明"); s.fillBlank(a,b); } }
運(yùn)行結(jié)果如下:
小明心算:1+1=2
該過程完全由Student類的實(shí)例對象單獨(dú)完成,并未涉及回調(diào)機(jī)制。
第二章.幼師的找茬
課間,幼師突發(fā)奇想在黑板上寫了"168+291=",讓小明完成,然后回辦公室去了。
這時候小明明顯不能再像上面那樣靠心算來完成了,正在懵逼的時候,班上的小紅同學(xué)遞過來一個只能計算加法的計算機(jī),而小明同學(xué)恰好知道怎么用計算器,于是通過計算器計算得到結(jié)果并完成了填空。
計算器的代碼為:
public class Calculator { public int add(int a,int b) { return a+b; } }
修改Student類,添加使用計算器的方法:
public class Student { private String name=null; public Student(String name) { this.name=name; } public void setName(String name) { this.name=name; } private int calcADD(int a,int b) { return a+b; } private int useCalculator(int a,int b) { return new Calculator().add(a,b); } public void fillBlank(int a,int b) { int result=useCalculator(a,b); System.out.println(name+"使用計算器:"+a+"+"+b+"="+result); } }
測試代碼如下:
public class Test { public static void main(String[] args) { int a=168; int b=291; Student s=new Student("小明"); s.fillBlank(a,b); } }
運(yùn)行結(jié)果如下:
小明使用計算器:168+291=459
該過程中仍未涉及到回調(diào)機(jī)制,但是小明的部分工作已經(jīng)實(shí)現(xiàn)了轉(zhuǎn)移,由計算器來協(xié)助實(shí)現(xiàn)。
第三章.幼師回來了
發(fā)現(xiàn)小明完成了3位數(shù)的加法,老師覺得小明很聰明,是個可塑之才。于是又在黑板上寫下了"26549+16387=",讓小明上課之前完成填空,然后又回辦公室了。
小明看著小紅再一次遞上來的計算機(jī),心生一計:讓小紅代勞。
小明告訴小紅題目是"26549+16487=",然后指出填寫結(jié)果的具體位置,然后就出去快樂的玩耍了。
這里,不把小紅單獨(dú)實(shí)現(xiàn)出來,而是把這個只能算加法的計算器和小紅看成一個整體,一個會算結(jié)果還會填空的超級計算器。折這個超級計算器需要傳的參數(shù)是兩個加數(shù)和要填空的位置,而這些內(nèi)容需要小明提前告知,也就是小明要把自己的一部分方法暴露給小紅,最簡單的方法就是把自己的引用和兩個加數(shù)一塊告訴小紅。因此,超級計算器的add方法應(yīng)該包含兩個操作數(shù)和小明自身的引用,
代碼如下:
public class SuperCalculator { public void add(int a,int b,Student xiaoming) { int result=a+b; xiaoming.fillBlank(a,b,result); } }
小明這邊現(xiàn)在已經(jīng)不需要心算,也不需要使用計算器,因此只需要有一個方法可以向小紅尋求幫助就行了,
代碼如下:
public class Student { private String name=null; public Student(String name) { this.name=name; } public void setName(String name) { this.name=name; } public void callHelp(int a,int b) { new SuperCalculator().add(a,b,this); } public void fillBlank(int a,int b,int result) { System.out.println(name+"求助小紅計算:"+a+"+"+b+"="+result); } }
測試代碼如下:
public class Test { public static void main(String[] args) { int a=26549; int b=16487; Student s=new Student("小明"); s.callHelp(a,b); } }
運(yùn)行結(jié)果為:
小明求助小紅計算:26549+16487=43036
執(zhí)行流程為:小明通過自身的callHelp方法調(diào)用了小紅(new SuperCalculator())的add方法,在調(diào)用的時候?qū)⒆陨淼囊?this)當(dāng)作參數(shù)一并傳入,小紅在使用計算器得出結(jié)果之后,回調(diào)了小明的fillBlank方法,將結(jié)果填在了黑板的空格上。
到這里,回調(diào)功能就正式登場了,小明的fillBlank方法就是我們常說的回調(diào)函數(shù)。
通過這種方式,可以明顯的看出,對于完成老師的填空題這個問題上,小明已經(jīng)不需要等待到加法做完且結(jié)果填寫在黑板上才能去跟小伙伴撒歡了,填空這個工作由超級計算器小紅來做了。回調(diào)的優(yōu)勢已經(jīng)開始體現(xiàn)了。
第四章.門口的婆婆
幼稚園的門口有一個頭發(fā)花白的老婆婆,每天風(fēng)雨無阻在那里擺著地攤賣一些快過期的垃圾食品。由于年紀(jì)大了,腦子有些糊涂,經(jīng)常算不清楚自己掙了多少錢。有一天,她無意間聽到了小明跟小伙伴們吹噓自己如何在小紅的幫助下與幼師斗智斗勇。于是,婆婆決定找到小紅牌超級計算器來做自己的小幫手,并提供一包衛(wèi)龍辣條作為報酬。小紅經(jīng)不住誘惑,答應(yīng)了。
回看一下上一章的代碼,我們發(fā)現(xiàn)小紅牌超級計算器的add方法需要的參數(shù)是兩個整型變量和一個Student對象,但是老婆婆她不是學(xué)生,是個小商販啊,這里肯定要做修改。這種情況下,我們很自然的會想到繼承和多態(tài)。如果讓小明這個學(xué)生和老婆婆這個小商販從一個父類進(jìn)行繼承,那么我們只需要給小紅牌超級計算器傳入一個父類的引用就可以啦。
不過,實(shí)際使用中,考慮到j(luò)ava的單繼承,以及不希望把自身太多東西暴漏給別人,這里使用從接口繼承的方式配合內(nèi)部類來做。
換句話說,小紅希望以后繼續(xù)向班里的小朋友們提供計算服務(wù),同時還能向老婆婆提供算賬服務(wù),甚至以后能夠拓展其他人的業(yè)務(wù),于是她向所有的顧客約定了一個辦法,用于統(tǒng)一的處理,也就是自己需要的操作數(shù)和做完計算之后應(yīng)該怎么做。這個統(tǒng)一的方法,小紅做成了一個接口,提供給了大家,
代碼如下:
public interface doJob { public void fillBlank(int a,int b,int result); }
因為靈感來自幫小明填空,因此小紅保留了初心,把所有業(yè)務(wù)當(dāng)做填空(fillBlank)來做。
同時,小紅修改了自己的計算器,使其可以同時處理不同的實(shí)現(xiàn)了doJob接口的人,代碼如下:
public class SuperCalulator { public void add(int a,int b doJob customer) { int result=a+b; customer.fillBlank(a,b,result); } }
小明和老婆婆拿到這個接口之后,只要實(shí)現(xiàn)了這個接口,就相當(dāng)于按照統(tǒng)一的模式告訴小紅得到結(jié)果之后的處理辦法,按照之前說的使用內(nèi)部類來做,代碼如下:
小明的:
public class Student { private String name=null; public Student(String name) { this.name=name; } public class doHomeWork implements doJob { @Override public void fillBlank(int a,int b,int result) { System.out.println(name+"求助小紅計算:"+a+"+"+b+"="+result); } } public void callHelp(int a,int b) { new SuperCalculator().add(a,b,new doHomeWork()); } }
老婆婆的:
public class Seller { private String name=null; public Seller(String name) { this.name=name; } public setName(String name) { this.name=name; } public class doHomeWork implements doJob { @Override public void fillBlank(int a,int b,int result) { System.out.println(name+"求助小紅算賬:"+a+"+"+b+"="+result+"元";) } } public void callHelp(int a,int b) { new SuperCalculator().add(a,b,new doHomeWork()); } }
測試程序如下:
public class Test { public static void main(String[] args) { int a=56; int b=31; int c=26497; int d=11256; Student s1=new Student("小明"); Seller s2=new Seller("老婆婆"); s1.callHelp(a,b); s2.callHelp(c,d); } }
運(yùn)行結(jié)果如下:
小明求助小紅計算:56+31=87
老婆婆求助小紅算賬:26497+11256=37753元
到此這篇關(guān)于Java 回調(diào)callback舉例詳解的文章就介紹到這了,更多相關(guān)Java 回調(diào)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
談?wù)凷pring 注入properties文件總結(jié)
本篇談?wù)凷pring 注入properties文件總結(jié),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-01-01Java實(shí)現(xiàn)拓?fù)渑判蛩惴ǖ氖纠a
在圖論中,拓?fù)渑判颍═opological Sorting)是一個有向無環(huán)圖(DAG, Directed Acyclic Graph)的所有頂點(diǎn)的線性序列。本文將為大家講講拓?fù)渑判蛩惴ǖ脑砑皩?shí)現(xiàn),需要的可以參考一下2022-07-07SpringBoot集成Zipkin實(shí)現(xiàn)分布式全鏈路監(jiān)控
這篇文章主要介紹了SpringBoot集成Zipkin實(shí)現(xiàn)分布式全鏈路監(jiān)控的方法啊,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-09-09關(guān)于Spring?Data?Jpa?自定義方法實(shí)現(xiàn)問題
這篇文章主要介紹了關(guān)于Spring?Data?Jpa?自定義方法實(shí)現(xiàn)問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12java的url方式、本地方式獲取json文件內(nèi)容
這篇文章給大家分享了java的url方式、本地方式獲取json文件內(nèi)容的實(shí)例代碼,有需要的朋友參考學(xué)習(xí)下。2018-07-07Java利用future及時獲取多線程運(yùn)行結(jié)果
在Java編程中,有時候會需要及時獲取線程的運(yùn)行結(jié)果,本文就通過一個相關(guān)實(shí)例向大家介紹Java利用future及時獲取線程運(yùn)行結(jié)果的方法,需要的朋友可以參考。2017-10-10springboot中JSONObject遍歷并替換部分json值
這篇文章主要介紹了springboot中JSONObject遍歷并替換部分json值,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11IDEA2020.1個性化設(shè)置的實(shí)現(xiàn)
這篇文章主要介紹了IDEA2020.1個性化設(shè)置的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08spring boot實(shí)現(xiàn)profiles動態(tài)切換的示例
Spring Boot支持在不同的環(huán)境下使用不同的配置文件,該技術(shù)非常有利于持續(xù)集成,在構(gòu)建項目的時候只需要使用不同的構(gòu)建命令就可以生成不同運(yùn)行環(huán)境下war包,而不需要手動切換配置文件。2020-10-10詳解Spring中使用xml配置bean的細(xì)節(jié)
本篇文章主要介紹了Spring中使用xml配置bean的細(xì)節(jié),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06