Java设计模式之回调函数
严格来说,回调函数并不属于23种设计模式中的任何一种。但是,回调函数在微服务中的使用越来越多,所以今天再来总结复习一下。
回调的意义在于,通过在其他对象中调用自身类中定义的函数,达到一定的目的(常见于事件注册,监听以及线程Runnable中的run)。
一、模块间调用
在一个应用系统中,无论使用何种语言开发,必然存在模块之间的调用,调用的方式分为几种:
(1)同步调用
同步调用是最基本并且最简单的一种调用方式,类A的方法a()调用类B的方法b(),一直等待b()方法执行完毕,a()方法继续往下走。这种调用方式适用于方法b()执行时间不长的情况,因为b()方法执行时间一长或者直接阻塞的话,a()方法的余下代码是无法执行下去的,这样会造成整个流程的阻塞。
(2)异步调用
异步调用是为了解决同步调用可能出现阻塞,导致整个流程卡住而产生的一种调用方式。类A的方法方法a()通过新起线程的方式调用类B的方法b(),代码接着直接往下执行,这样无论方法b()执行时间多久,都不会阻塞住方法a()的执行。但是这种方式,由于方法a()不等待方法b()的执行完成,在方法a()需要方法b()执行结果的情况下(视具体业务而定,有些业务比如启异步线程发个微信通知、刷新一个缓存这种就没必要),必须通过一定的方式对方法b()的执行结果进行监听。在Java中,可以使用Future+Callable的方式做到这一点。
(3)回调
最后是回调,回调的思想是:
- 类A的a()方法调用类B的b()方法
- 类B的b()方法执行完毕主动调用类A的callback()方法
这样一种调用方式组成了上图,也就是一种双向的调用方式。所谓回调:就是A类中调用B类中的某个方法b(),然后B类中反过来调用A类中的方法callback(),callback()这个方法就叫回调方法。
二、回调结构:
- callback接口:定义回调方法
- class A:实现接口callback,包含一个class B的引用b
- class B:有一个参数为callback的方法func(Callback callback)
三、代码示例(一)
3.1、callback接口:定义回调方法
package cn.lxk.callback02;/*** 回调接口*/
public interface Callback {// 回调方法public void tellAnswer(String answer);
}
3.2、class A:实现接口callback,包含一个class B的引用b
package cn.lxk.callback02;/*** 老师对象*/
public class Teacher implements Callback {private Student student;public Teacher(Student student) {this.student = student;}public void askQuestion() {System.out.println("老师:1+2=?");new Thread(()->student.resolveQuestion(this)).start();}@Overridepublic void tellAnswer(String answer) {System.out.println(answer);}public void doSomething() {System.out.println("老师去做其他事情。。。");;}
}
3.3、定义class B的接口(面向接口编程,可省)
package cn.lxk.callback02;/*** 学生接口*/
public interface Student {public void resolveQuestion(Callback callback);}
3.4、class B:有一个参数为callback的方法func(Callback callback)
package cn.lxk.callback02;/*** 一个名叫Ricky的同学解决老师提出的问题*/
public class Ricky implements Student {@Override// 注册回调public void resolveQuestion(Callback callback) {// 模拟解决问题try {Thread.sleep(3000);} catch (InterruptedException e) {}callback.tellAnswer("学生:我知道了,你的答案是3(想了3秒钟)");}
}
测试类:
package cn.lxk.callback02;public class MainClass {public static void main(String[] args) {Student student = new Ricky();Teacher teacher = new Teacher(student);teacher.askQuestion();teacher.doSomething();}
}
测试结果:
老师:1+2=?
老师去做其他事情。。。
学生:我知道了,你的答案是3(想了3秒钟)
简单总结、分析一下这个例子就是:
(1)老师调用学生接口的方法resolveQuestion,向学生提问
(2)学生解决完毕问题之后调用老师的回调方法tellAnswer
这样一套流程,构成了一种双向调用的关系。
四、代码示例(二)--常见用法,回调函数调用时定义(注册)
4.1、定义数据
package cn.lxk.callback;public class Data {private int n;private int m;public Data(int n, int m) {System.out.println("调用data的构造函数");this.n = n;this.m = m;}@Overridepublic String toString() {int r = n / m;return n + "/" + m + " = " + r;}public int getN() { return n; }public void setN(int n) { this.n = n; }public int getM() { return m; }public void setM(int m) { this.m = m; }
}
4.2、定义回调函数接口
package cn.lxk.callback;public interface FetcherCallback {void onData(Data data) throws Exception;void onError(Throwable cause);
}
4.3、 定义class B的接口(面向接口编程,可省)
package cn.lxk.callback;public interface Fetcher {void fetchData(FetcherCallback callback);
}
4.4、 class B:有一个参数为callback的方法func(Callback callback)
package cn.lxk.callback;public class MyFetcher implements Fetcher {final Data data;public MyFetcher(Data data) {System.out.println("调用MyFetcher的构造函数");this.data = data;}// 此方法接受一个对象@Overridepublic void fetchData(FetcherCallback callback) {try {//正常情况System.out.println("调用fetchData方法正常");// 注册回调函数,但还没有实现callback.onData(data);} catch (Exception e) {//报错情况System.out.println("调用fetchData方法异常");// 注册回调函数,但还没有实现callback.onError(e);}}
}
4.5、class A虽未实现接口callback,当通过类B(MyFetcher类),访问其中的fetchData()方法
package cn.lxk.callback;public class Worker {
/*** Fetcher.fetchData()方法需传递一个FetcherCallback类型的参数,当获得数据或发生错误时被回调。
对于每种情况都提供了同意的方法:
• FetcherCallback.onData(),将接收数据时被调用
• FetcherCallback.onError(),发生错误时被调用因为可以将这些方法的执行从"caller"线程移动到其他的线程执行;但也不会保证FetcherCallback
的每个方法都会被执行。回调过程有个问题就是当你使用链式调用很多不同的方法会导致线性代码;有些人认为
这种链式调用方法会导致代码难以阅读,但是我认为这是一种风格和习惯问题。例如,基于Javascript的
Node.js越来越受欢迎,它使用了大量的回调,许多人都认为它的这种方式利于阅读和编写。*/public void doWork() {System.out.println("调用doWork方法");Fetcher fetcher = new MyFetcher(new Data(1, 0));fetcher.fetchData(new FetcherCallback() {@Overridepublic void onError(Throwable cause) {System.out.println("错误回调: " + cause.getMessage());}@Overridepublic void onData(Data data) {System.out.println("正常回调: " + data.toString());}});}public static void main(String[] args) {Worker w = new Worker();w.doWork();}
}
测试结果:
正常常景:
调用doWork方法
调用data的构造函数
调用MyFetcher的构造函数
调用fetchData方法正常
正常回调: 1/2 = 0异常场景:
调用doWork方法
调用data的构造函数
调用MyFetcher的构造函数
调用fetchData方法正常
调用fetchData方法异常
错误回调: / by zero
五、应用(多线程)
package cn.lxk.futures;import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;/*** 有时候使用Future感觉很丑陋,因为你需要间隔检查Future是否已完成,而使用回调会直接收到返回通知。*/
public class FutureExample {public static void main(String[] args) throws Exception {// 创建线程池ExecutorService executor = Executors.newCachedThreadPool();// 创建线程Runnable task1 = new Runnable() {@Overridepublic void run() {// do somethingSystem.out.println("第一个runnable线程.....");}};// 创建线程Callable<Integer> task2 = new Callable<Integer>() {@Overridepublic Integer call() throws Exception {// do somethingSystem.out.println("第二个Callable线程……");return new Integer(100);}};// 提交第一个线程Future<?> f1 = executor.submit(task1);// 提交第二个线程Future<Integer> f2 = executor.submit(task2);// 打印第1个线程是否结束System.out.println("第1个线程是否结束? " + f1.isDone());// 打印第2个线程是否结束System.out.println("第2个线程是否结束? " + f2.isDone());// 等待第一个线程结束while (f1.isDone()) {System.out.println("第一个线程结束");break;}// 等待第二个线程结束while (f2.isDone()) {System.out.println("第二个线程结束,返回结果: " + f2.get());break;}}}
测试结果:
参考:https://www.cnblogs.com/xrq730/p/6424471.html
Java设计模式之回调函数相关推荐
- Java中的回调函数学习-深入浅出
Java中的回调函数一般来说分为下面几步: 声明回调函数的统一接口interface A.包括方法callback(); 在调用类caller内将该接口设置为私有成员private A XXX; 在c ...
- java有没有回调函数_Java中的回调函数 - wangjianme的个人空间 - OSCHINA - 中文开源技术交流社区...
Java代码的回调函数经常由框架或是系统定义,由程序开发人员填充. 它的最主要特点是即定义了调用的规范同时又非常的灵活. 回调函数有些类似于观察者模式,它们的区别在于:观察者模式返回的参数为Event ...
- java socket 异步回调函数,分享nodejs异步编程基础之回调函数用法
nodejs异步编程基础之回调函数用法分析 本文实例讲述了nodejs异步编程基础之回调函数用法.分享给大家供大家参考,具体如下: Node.js 异步编程的直接体现就是回调. 异步编程依托于回调来实 ...
- java socket 异步回调函数_浅谈socket同步和异步、阻塞和非阻塞、I/O模型
原标题:浅谈socket同步和异步.阻塞和非阻塞.I/O模型 在进行网络编程时,常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式 同步/异步主要针 ...
- Android和Java中的回调函数
在 Android 的学习过程中遇到了回调函数,经过一段时间的理解,将自己的收获整理如下,希望对迫切希望了解这方面知识的同学有所启发. 回调函数的理解如下: 在A类中定义了一个方法,这个方法中用到了一 ...
- swig c java gemt,SWIG C函数指针和JAVA
我在C中有一些代码,其中一个方法有一个函数指针作为参数.我正在尝试在我的 Android应用中使用C代码. 我决定使用SWIG来完成生成我需要的java文件的所有工作.一切都适用于常规函数(没有函数指 ...
- java中函数的调用,java中如何调用函数
java动态调用函数,Java 中使用动态代码,java函数调用,java中如何调用函数 如何在 Java 中调用 C 函数 宗薇 [期刊名称]<网络新媒体技术> [年(卷),期]2000 ...
- Java设计模式-回调函数和观察者模式
Android的框架中有非常多的地方使用了回调的模式,例如Activity的生命周期,按钮的点击事件等. 下面是回调的基本模型: public class A {private CallBack ca ...
- 回调函数、Java接口回调 总结
回调函数 谈到回调,我们得先从回调函数说起,什么叫回调函数呢? 回调函数是什么? 百度百科的解释:回调函数就是一个通过函数指针调用的函数.如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针 ...
最新文章
- 面试:你知道 Java 中的回调机制吗?
- 单链表-逆置单链表(头插法且双指针)
- [LUOGU] P3128 [USACO15DEC]最大流Max Flow
- vs2005's addin folder
- Label显示多行文本总结
- 影视剧中的歌曲怎么录制 怎么录背景音乐
- 迅雷防踢补丁:一个刷流量木马的简单分析
- Python -- 网络编程 -- 抓取网页图片 -- 豆瓣妹子
- Idea终端中无法使用maven命令问题解决
- select_related和prefetch_related的用法与区别
- python面试题-如“上海 深圳 深圳 上海“,要求输入一个匹配模式,比如: aabb,判断是否符合
- matlab读取TXT文件数据,二进制文件数据
- Codeforces 985A. Chess Placing(1ni)(水题)(div.2)
- labelImg打开闪退怎么解决
- Proxmox VE ZFS 开启Thin Provision(精简配置)
- 用Excel分析音视频同步
- js调用linux命令行,shelljs
- Retrofit简介
- PHP 简单案例[5]
- 关于elasticsearch.yml的配置