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