標籤:已耗用時間 cut oid 定義 too tac callback ide rgs
回呼函數,或簡稱回調,是指通過函數參數傳遞到其它代碼的,某一塊可執行代碼的引用。這一設計允許了底層代碼調用在高層定義的子程式。
在Java裡面,我們使用介面來實現回調。舉個例子
所謂的回調,就是程式員A寫了一段程式(程式a),其中預留有回呼函數介面,並封裝好了該程式。程式員B要讓a調用自己的程式b中的一個方法,於是,他通過a中的介面回調自己b中的方法。
舉個例子:
1. 首先定義一個類Caller,按照上面的定義就是程式員A寫的程式a,這個類裡面儲存一個介面引用。
public class Caller { private MyCallInterface callInterface; public Caller() { } public void setCallFunc(MyCallInterface callInterface) { this.callInterface = callInterface; } public void call() { callInterface.printName(); }}
2. 介面的定義,方便程式員B根據定義編寫程式實現介面。
public interface MyCallInterface { public void printName();}
3. 第三是定義程式員B寫的程式b
public class Client implements MyCallInterface { @Override public void printName() { System.out.println("This is the client printName method"); }}
4. 測試
public class Test { public static void main(String[] args) { Caller caller = new Caller(); caller.setCallFunc(new Client()); caller.call(); }}
這樣我們可以看到程式a中保留有介面成員變數,使得程式a可以通過這個介面變數調用這個介面任意實作類別的方法。而程式b被調用的方法就是回呼函數。
接下來在看一個具體的實現:
下面使用java回呼函數來實現一個測試函數已耗用時間的工具類:
如果我們要測試一個類的方法的執行時間,通常我們會這樣做:
public class TestObject { /** * 一個用來被測試的方法,進行了一個比較耗時的迴圈 */ public static void testMethod(){ for ( int i= 0 ; i< 100000000 ; i++){ } } /** * 一個簡單的測試方法執行時間的方法 */ public void testTime(){ long begin = System.currentTimeMillis(); //測試起始時間 testMethod(); //測試方法 long end = System.currentTimeMillis(); //測試結束時間 System.out.println("[use time]:" + (end - begin)); //列印使用時間 } public static void main(String[] args) { TestObject test=new TestObject(); test.testTime(); } }
下面我們來做一個函數實現相同功能但更靈活:
首先定一個回調介面:
public interface CallBack { //執行回調操作的方法 void execute(); }
然後再寫一個工具類:
public class Tools { /** * 測試函數使用時間,通過定義CallBack介面的execute方法 * @param callBack */ public void testTime(CallBack callBack) { long begin = System.currentTimeMillis(); //測試起始時間 callBack.execute(); ///進行回調操作 long end = System.currentTimeMillis(); //測試結束時間 System.out.println("[use time]:" + (end - begin)); //列印使用時間 } public static void main(String[] args) { Tools tool = new Tools(); tool.testTime(new CallBack(){ //定義execute方法 public void execute(){ //這裡可以加放一個或多個要測試回合時間的方法 TestObject.testMethod(); } }); } }
一個待測試的,較耗時的方法:
public class TestObject { /** * 一個用來被測試的方法,進行了一個比較耗時的迴圈 */ public static void testMethod(){ for ( int i= 0 ; i< 100000000 ; i++){ } }}
這裡我們沒有寫程式b去實現Callback介面,而是通過匿名內部類的方法來實現。同樣也實現了回呼函數。
之後我們看看為什麼要使用回呼函數:
所謂回呼函數就是A調用了B,B在適當的時候又反回去調用A。多數時候因為是單線程,A沒有必要等B來調用它,因為A在調用完B之後完全可以調用自己需要的操作。所以回調多見於事件驅動機制裡。因為A在調用完B之後不知道B什麼時候會完成,所以A不知道B什麼時候會完成。而唯一知道B什麼時候完成的當然是B自己了,所以當B完成的時候會通過一個回呼函數通知A,自己已經完成了,這時候A才知道該進行下面的操作。如果不這樣的話,A就只能不斷地詢問B是不是已經完成了(就是輪詢),可見是效率非常低的,實現也很麻煩。
回調通常是在兩個不同的線程間需要同步的情況下才出現的,但是很多時候又沒有必要用訊號量去進行真正的線程同步,因為會很複雜,而且沒有必要。所以有了回調。至於回調要乾的事情,當然是你自己決定了。
說一下同步回調和非同步回調:
同步指的是調用一個方法,調用方要等待該方法所執行的任務完全執行完畢,然後控制權回到調用方;非同步指的是調用一個方法,調用方不等該方法執行的任務完畢就返回,當任務執行完畢時會自動執行調用方傳入的一塊代碼。
同步:
void runTask { doTask1() doTask2()}
同步調用,執行完 doTask1 在執行 doTask2
非同步
doTask1(new Callback() { void call() { doTask3() }});doTask2();
非同步回調,會同時執行 doTask1 和 doTask2, 在執行完 doTask1 後執行 doTask3
同步調用適合執行耗時短的任務,非同步回調適合執行耗時間長度的任務,而且調用它之後調用的任務沒什麼關係。
看一個非同步回調的案例:
回調介面類:
/** * 回調模式-回調介面類 */public interface CSCallBack { public void process(String status);}
類比用戶端:
/** * 回調模式-類比用戶端類 */public class Client implements CSCallBack { private Server server; public Client(Server server) { this.server = server; } public void sendMsg(final String msg){ System.out.println("用戶端:發送的訊息為:" + msg); new Thread(new Runnable() { @Override public void run() { server.getClientMsg(Client.this,msg); } }).start(); System.out.println("用戶端:非同步發送成功"); } @Override public void process(String status) { System.out.println("用戶端:服務端回調狀態為:" + status); }}
類比服務端:
/** * 回調模式-類比服務端類 */public class Server { public void getClientMsg(CSCallBack csCallBack , String msg) { System.out.println("服務端:服務端接收到用戶端發送的訊息為:" + msg); // 類比服務端需要對資料處理 try { Thread.sleep(5 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("服務端:資料處理成功,返回成功狀態 200"); String status = "200"; csCallBack.process(status); }}
測試類別:
/** * 回調模式-測試類別 */public class CallBackTest { public static void main(String[] args) { Server server = new Server(); Client client = new Client(server); client.sendMsg("Server,Hello~"); }}
Java回呼函數的理解與實現