Java中的回調(diào)
又忙了一周,事情差不多解決了,終于有可以繼續(xù)寫我的博客了(各位看官久等了)。
這次我們來談一談Java里的一個(gè)很有意思的東西——回調(diào)。
什么叫回調(diào),一本正經(jīng)的來講,在計(jì)算機(jī)程序設(shè)計(jì)中,回調(diào)函數(shù)是指通過函數(shù)參數(shù)傳遞到其它代碼的,某一塊可執(zhí)行代碼的引用。這一設(shè)計(jì)允許了底層代碼調(diào)用在高層定義的子程序。
別急別急,且聽我慢慢道來。
舉個(gè)栗子,設(shè)置這樣一個(gè)情景,老板安排員工做事,然后讓他做完后跟他電話說一聲。老板當(dāng)然不會(huì)在那里一直等員工做完事情才去做其他事,而是只交代完任務(wù)就去忙自己的事情了。
這個(gè)例子包含了異步+回調(diào)的思想,員工做完任務(wù)后向老板報(bào)告這個(gè)過程,就叫回調(diào),當(dāng)然,報(bào)告的話,老板肯定先跟員工說好了報(bào)告方式,比如說郵件,電話等,而交代報(bào)告方式,就是注冊(cè)回調(diào)函數(shù),這里的回調(diào)函數(shù)必須符合接口的規(guī)范。
好像還是有些不明白?來上代碼吧。
先定義一個(gè)接口:.
public interface ReceiveReport {
/**
* 接收?qǐng)?bào)告
* @param name 員工名稱
* @param report 報(bào)告內(nèi)容
*/
public void receiveReport(String name,String report);
}
定義一個(gè)Boss類實(shí)現(xiàn)這個(gè)接收?qǐng)?bào)告的接口:
public class Boss implements ReceiveReport{
private Worker worker;
public Boss(Worker worker){
this.worker = worker;
}
/**
* 下達(dá)任務(wù)
*/
public void sendTask(){
worker.work(this);
}
/**
* 接收?qǐng)?bào)告
* @param name 員工名稱
* @param report 報(bào)告內(nèi)容
*/
public void receiveReport(String name,String report){
System.out.println("收到:"+name+" 的報(bào)告:"+report);
}
}
定義一個(gè)Worker接口:
public interface Worker {
public void work(ReceiveReport boss);
}
定義一個(gè)員工類。
public class Employee implements Worker{
private String name;//員工姓名
//構(gòu)造器
public Employee(String name) {
this.name = name;
}
/**
* 工作
* @param boss 任務(wù)名稱
*/
public void work(ReceiveReport boss){
System.out.println(name + " is doing works.");
String report = "我已經(jīng)完成了任務(wù)!";
boss.receiveReport(name,report);
}
}
然后來測試一下:
public class Test {
public static void main(String[] args) {
Worker employee = new Employee("Frank");//定義一個(gè)員工
Boss boss = new Boss(employee);//定義一個(gè)Boss
//boss開始下達(dá)任務(wù)
boss.sendTask();
}
}
測試結(jié)果:
Frank is doing works.收到:Frank 的報(bào)告:我已經(jīng)完成了任務(wù)!
至此,員工與老板的交互就完成了,這就是一個(gè)簡單的同步回調(diào)了。Boss通過Worker接口可以給員工安排工作,而不用去關(guān)心是哪個(gè)員工在工作,Worker通過ReceiveReport來向Boss報(bào)告工作情況,兩個(gè)類通過接口進(jìn)行回調(diào)交互,可以很好的解耦合,因?yàn)锽oss可以安排不同的員工,只要他們實(shí)現(xiàn)了Worker接口就行,而員工也可以向不同的boss匯報(bào)情況,只要實(shí)現(xiàn)了ReceiveReport接口即可。
其實(shí)回調(diào)的核心思想就是把自身的this指針傳給調(diào)用方,就像這里把employee傳入Boss類中,在work方法中又注冊(cè)了回調(diào),于是兩者的交互性就很強(qiáng)了。
那么為什么要用回調(diào)呢?如果Boss要在員工完成工作之前登記員工的一些信息,如姓名等,那么有了回調(diào)機(jī)制,通過把this指針傳入,就能在Boss內(nèi)部為所欲為了,而不需要通過設(shè)計(jì)新的方法來獲取,而且需要獲得的數(shù)據(jù)越多,回調(diào)的優(yōu)勢越明顯。
其實(shí)這里只是簡單的一對(duì)一關(guān)系,如果是一個(gè)Boss,多個(gè)員工,那就是簡單的觀察者模式,如果是多個(gè)Boss多個(gè)員工,那就是簡單生產(chǎn)者-消費(fèi)者模式了。
當(dāng)然,這里僅僅是簡單的同步回調(diào)。員工只能一個(gè)接一個(gè)的去完成任務(wù),也就是說前一個(gè)員工必須等待后一個(gè)員工完成任務(wù)后才能開始任務(wù),事實(shí)上,員工一般是同時(shí)進(jìn)行工作的。
如果換一個(gè)場景,現(xiàn)在有十個(gè)員工,老板發(fā)布任務(wù),前三名完成的人有獎(jiǎng)金獎(jiǎng)勵(lì),那么就需要用到異步回調(diào)了,sendTask的時(shí)候使用線程即可,我們來修改一下代碼:
/**
* @author Frank
* @create 2017/12/3
* @description 接收?qǐng)?bào)告接口
*/
public interface ReceiveReport {
/**
* 接收?qǐng)?bào)告
* @param worker 員工
* @param report 報(bào)告內(nèi)容
*/
public void receiveReport(Worker worker,String report);
}
/**
* @author Frank
* @create 2017/12/3
* @description 工人接口
*/
public interface Worker {
public void work(String taskName);
public void setReceiveReport(ReceiveReport boss);
public void getReward(Double money);
public String getName();
}
import java.util.Random;
/**
* @author Frank
* @create 2017/12/3
* @description 員工類
*/
public class Employee implements Worker{
private ReceiveReport boss;
private String name;//員工姓名
@Override
public String getName() {
return name;
}
//構(gòu)造器
public Employee(String name) {
this.name = name;
}
public void setReceiveReport(ReceiveReport boss) {
this.boss = boss;
}
@Override
public void getReward(Double money) {
System.out.println(name+"由于表現(xiàn)突出,獲得$"+money+"現(xiàn)金獎(jiǎng)勵(lì)!");
}
/**
* 工作
* @param taskName 任務(wù)名稱
*/
public void work(String taskName){
System.out.println(name + " is doing works:"+taskName);
Random random = new Random();
Integer time = random.nextInt(10000);
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
String report = "順利完成任務(wù)!";
//通知老板
boss.receiveReport(this,report);
}
}
import java.util.ArrayList;
import java.util.List;
/**
* @author Frank
* @create 2017/12/3
* @description Boss類
*/
public class Boss implements ReceiveReport{
private List<Worker> workers = new ArrayList<>();//老板管理的員工
private volatile int index;//順序
/**
* 添加員工
* @param worker 員工
*/
public void addWorker(Worker worker){
workers.add(worker);
worker.setReceiveReport(this);
}
/**
* 下達(dá)任務(wù)
*/
public void sendTask(String task){
//給各個(gè)員工依次下達(dá)任務(wù)
for (Worker w:workers){
new Thread(new Runnable() {
@Override
public void run() {
w.work(task);
}
}).start();
}
}
/**
* 接收?qǐng)?bào)告
* @param worker 員工
* @param report 報(bào)告內(nèi)容
*/
public void receiveReport(Worker worker,String report){
int index = ++this.index;
System.out.println(worker.getName()+"獲得第"+index+"名");
if (index <= 3){
//給前三名發(fā)獎(jiǎng)金
worker.getReward(1000.0*(4-index));
}
}
}
/**
* @author Frank
* @create 2017/12/3
* @description
*/
public class Test {
public static void main(String[] args) {
Boss boss = new Boss();//定義一個(gè)Boss
//定義十個(gè)員工
for (int i=0;i<10;i++){
Worker worker = new Employee("Employee["+i+"]");
boss.addWorker(worker);
}
//boss開始下達(dá)任務(wù)
boss.sendTask("Say Hello");
}
}
這里沒有使用鎖,因?yàn)樵O(shè)置的時(shí)間間隔區(qū)間為0-10s,發(fā)生并發(fā)沖突的概率很低,而且由于現(xiàn)在還沒有說多線程的內(nèi)容,所以暫時(shí)先不使用。只需要知道在sendTask方法中,依次啟動(dòng)了線程來調(diào)用每個(gè)Worker的work方法,線程啟動(dòng)后會(huì)同時(shí)執(zhí)行,執(zhí)行完畢后,又會(huì)調(diào)用Boss的receiveReport方法來向Boss反饋結(jié)果,接收結(jié)果后,根據(jù)完成順序,再調(diào)用Worker的getReward方法來給前三名發(fā)獎(jiǎng)金。其實(shí)這里是雙向回調(diào)了,Boss把this指針傳給了Worker,Worker又把自己的this指針傳給了Worker。
程序執(zhí)行結(jié)果如下:
Employee[0] is doing works:Say Hello
Employee[4] is doing works:Say Hello
Employee[3] is doing works:Say Hello
Employee[2] is doing works:Say Hello
Employee[1] is doing works:Say Hello
Employee[5] is doing works:Say Hello
Employee[7] is doing works:Say Hello
Employee[6] is doing works:Say Hello
Employee[9] is doing works:Say Hello
Employee[8] is doing works:Say Hello
Employee[9]獲得第1名
Employee[9]由于表現(xiàn)突出,獲得$3000.0現(xiàn)金獎(jiǎng)勵(lì)!
Employee[7]獲得第2名
Employee[7]由于表現(xiàn)突出,獲得$2000.0現(xiàn)金獎(jiǎng)勵(lì)!
Employee[3]獲得第3名
Employee[3]由于表現(xiàn)突出,獲得$1000.0現(xiàn)金獎(jiǎng)勵(lì)!
Employee[1]獲得第4名
Employee[0]獲得第5名
Employee[5]獲得第6名
Employee[4]獲得第7名
Employee[8]獲得第8名
Employee[6]獲得第9名
Employee[2]獲得第10名
因?yàn)槭褂昧硕嗑€程,所以每次運(yùn)行的結(jié)果可能都會(huì)不一樣,如果得到了不一樣的結(jié)果,那是很正常的現(xiàn)象。
舉了這兩個(gè)栗子,對(duì)回調(diào)應(yīng)該也有了一定的了解了吧。
其實(shí)回調(diào)只是一種思想,并不是java中獨(dú)有的內(nèi)容,思想這種東西,是為了解決特定場景下的特定問題而出現(xiàn)的,只有被正確應(yīng)用了才有它的價(jià)值,而不要為了使用它而使用它。
至此,回調(diào)講解完畢,如有說明有誤的地方,歡迎各位批評(píng)指正。也歡迎大家繼續(xù)關(guān)注。
以上就是Java中的回調(diào)的詳細(xì)內(nèi)容,更多關(guān)于Java 回調(diào)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot中@Scheduled實(shí)現(xiàn)服務(wù)啟動(dòng)時(shí)執(zhí)行一次
本文主要介紹了SpringBoot中@Scheduled實(shí)現(xiàn)服務(wù)啟動(dòng)時(shí)執(zhí)行一次,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-08-08
MyBatis-plus更新對(duì)象時(shí)將字段值更新為null的實(shí)現(xiàn)方式
mybatis-plus在執(zhí)行更新操作,當(dāng)更新字段為 空字符串 或者 null 的則不會(huì)執(zhí)行更新,如果要將指定字段更新null,可以通過以下三種方式實(shí)現(xiàn),感興趣的小伙伴跟著小編一起來看看吧2023-10-10
解決新版idea新建文件沒有XML和Resource Bundle文件問題
這篇文章主要介紹了解決新版idea新建文件沒有XML和Resource Bundle文件問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07
淺談Servlet 實(shí)現(xiàn)網(wǎng)頁重定向的方法
本篇文章主要介紹了Servlet 實(shí)現(xiàn)重定向幾種方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-08-08
SpringBoot使用Micrometer實(shí)現(xiàn)度量和監(jiān)控
在構(gòu)建和維護(hù)現(xiàn)代應(yīng)用程序時(shí),度量和監(jiān)控是至關(guān)重要的,它們可以幫助您了解應(yīng)用程序的性能、穩(wěn)定性和可用性,本文將介紹如何在Spring Boot應(yīng)用程序中使用Micrometer進(jìn)行度量和監(jiān)控,需要的朋友可以參考下2023-10-10
java實(shí)現(xiàn)簡單控制臺(tái)通訊錄
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡單控制臺(tái)通訊錄,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02

