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