面试官:你知道 Java 中的回调机制吗?

news2024/9/30 1:28:28

调用和回调机制

在一个应用系统中, 无论使用何种语言开发, 必然存在模块之间的调用, 调用的方式分为几种。

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类中的某个方法C, 然后B类中反过来调用A类中的方法D, D这个方法就叫回调方法, 这样子说你是不是有点晕晕的, 其实我刚开始也是这样不理解, 看了人家说比较经典的回调方式:

  1. class A实现接口CallBack callback——背景1

  2. class A中包含一个class B的引用b ——背景2

  3. class B有一个参数为callback的方法f(CallBack callback) ——背景3

  4. A的对象a调用B的方法 f(CallBack callback) ——A类调用B类的某个方法 C

  5. 然后b就可以在f(CallBack callback)方法中调用A的方法 ——B类调用A类的某个方法D

回调的种类

回调分为同步回调和异步回调, 假如以买彩票的场景来模拟, 我买彩票, 调用彩票网,给我返回的结果确定是否中奖,同步回调就是,我买了彩票之后, 需要等待彩票网给我返回的结果, 这个时候我不能做其他事情, 我必须等待这个结果, 这就叫同步回调, 同步, 就意味着等待, 我不能去做其他事情, 必须等待, 异步回调就是, 我买了彩票之后, 可以去做其他事情, 然后当彩票网有了结果和消息, 再给我返回消息, 其中最明显的方式就是在得到彩票结果的函数之中, 添加一个其他的方法, 如果我的其他方法可以立即执行, 那么就是异步的(给出是否中奖需要花费很长的时间), 而在测试函数之中, 前后两个, 那是发生在测试函数的线程之中的, 肯定是一前一后按照次序的, 在这个地方不是显示同步异步的地点。

同步回调

同步回调和异步回调, 主要体现在其是否需要等待. 同步调用, 如果被调用一方的APi(第三方API), 处理问题需要花很长时间, 我们需要等待, 那就是同步回调, 如果调用完之后不需要理解得到结果, 我们调完就走, 去做其他事情, 那就是异步调用, 异步调用需要在我们调用第三方API处, 开启一个新的线程即可,而同步调用和平常的调用没有任何区别。

例子

OrderResult接口, 其中的方法getOrderResult

public interface OrderResult {
    /**
     * 订购货物的状态
     *
     * @param state
     * @return
     */
    //参数可以不用, 用不用按照自己的实际需求决定
    public String getOrderResult(String state);
}

Store类, 商店提供会无预定消息返回的接口, 回调OrderResult接口的方法, 给其返回预订商品的状态, 重点是returnOrderGoodsInfo(OrderResult order)方法, 体现了回调的回Store是被调用的一方, 被调用的一方, 要回过去调用调用一方的方法, 这个方法实际上是回调接口的方法.

public class Store {
    @Getter
    @Setter
    private String name;

    Store(String name) {
        this.name = name;
    }

    /*回调函数, 将结构传给那个我们不能直接调用的方法, 然后获取结果*/
    public String returnOrderGoodsInfo(OrderResult order) {
        String[] s = {"订购中...", "订购失败", "即将发货!", "运输途中...", "已在投递"};
        Random random = new Random();
        int temp = random.nextInt(5);
        String s1 = s[temp];
        return order.getOrderResult(s1);
    }
}

SyncBuyer类, 同步顾客类, 其中获取商品的订购状态,orderGoods(), 调用了store返回商品调用信息的returnOrderGoodsInfo()方法, 但是在Store类的returnOrderGoodsInfo()方法之中, 以OrderResult接口为参数, 反过来调用了OrderResult接口, 相当于调用了其子类SyncBuyer本身, 以他为参数, 调用了getOrderResult(String state)方法, 也就是OrderResult接口的方法, 相当于就完成了一个调用的循环, 然后取到了我们自己无法给出的结果。

这个地方的 "循环", 是回调的关键所在, 需要正常调用其他外接提供方法来获取结果的一方, 继承一个回调接口, 实现它, 然后调用第三方的API方法, 第三方在我们调用的方法之中, 以回调结构为参数, 然后调用了接口中的方法, 其中可以返回相应的结果给我们, 需要说明的是, 我们虽然实现了这个接口的方法, 但是我们自己的类之中, 或者说此类本身, 却没法调用这个方法, 也可以说, 此类调用这个方法是不会产生有效的结果的. 回调的回, 就体现在此处, 在Store类之中的returnOrderGoodsInfo(OrderResult order)方法之中, 得到了很好的体现.

/*同步, 顾客在商店预订商品, 商店通知顾客预订情况*/
public class SyncBuyer implements OrderResult {
    @Getter
    @Setter
    private Store store;//商店
    @Getter
    @Setter
    private String buyerName;//购物者名
    @Getter
    @Setter
    private String goodsName;//所购商品名

    SyncBuyer(Store store, String buyerName, String goodsName) {
        this.store = store;
        this.buyerName = buyerName;
        this.goodsName = goodsName;
    }

    /*调用从商店返回订购物品的信息*/
    public String orderGoods() {
        String goodsState = store.returnOrderGoodsInfo(this);
        System.out.println(goodsState);
        myFeeling();// 测试同步还是异步, 同步需要等待, 异步无需等待
        return goodsState;

    }

    public void myFeeling() {
        String[] s = {"有点小激动", "很期待!", "希望是个好货!"};
        Random random = new Random();
        int temp = random.nextInt(3);
        System.out.println("我是" + this.getBuyerName() + ", 我现在的感觉: " + s[temp]);
    }

    /*被回调的方法, 我们自己不去调用, 这个方法给出的结果, 是其他接口或者程序给我们的, 我们自己无法产生*/
    @Override
    public String getOrderResult(String state) {
        return "在" + this.getStore().getName() + "商店订购的" + this.getGoodsName() + "玩具, 目前的预订状态是: " + state;
    }
}
 

Test2Callback类, 测试同步回调的结果,

public class Test2Callback {
    public static void main(String[] args) {
        Store wallMart = new Store("沙中路沃尔玛");
        SyncBuyer syncBuyer = new SyncBuyer(wallMart, "小明", "超能铁扇公主");
        System.out.println(syncBuyer.orderGoods());
    }
}
 

异步回调

同步回调和异步回调的代码层面的差别就是是否在我们调用第三方的API处, 为其开辟一条新的线程, 其他并无差异例子

OrderResult接口, 其中的方法getOrderResult

public interface OrderResult {
    /**
     * 订购货物的状态
     *
     * @param state
     * @return
     */
    //参数可以不用, 用不用按照自己的实际需求决定
    public String getOrderResult(String state);
}

Store类, 商店提供会无预定消息返回的接口, 回调OrderResult接口的方法, 给其返回预订商品的状态.

public class Store {
    @Getter
    @Setter
    private String name;

    Store(String name) {
        this.name = name;
    }

    /*回调函数, 将结构传给那个我们不能直接调用的方法, 然后获取结果*/
    public String returnOrderGoodsInfo(OrderResult order) {
        String[] s = {"订购中...", "订购失败", "即将发货!", "运输途中...", "已在投递"};
        Random random = new Random();
        int temp = random.nextInt(5);
        String s1 = s[temp];
        return order.getOrderResult(s1);
    }
}

NoSyncBuyer类, 异步调用Store类的returnOrderGoodsInfo(OrderResult order)方法,来返回商品转改的结果。

/*异步*/
@Slf4j
public class NoSyncBuyer implements OrderResult {
    @Getter
    @Setter
    private Store store;//商店
    @Getter
    @Setter
    private String buyerName;//购物者名
    @Getter
    @Setter
    private String goodsName;//所购商品名

    NoSyncBuyer(Store store, String buyerName, String goodsName) {
        this.store = store;
        this.buyerName = buyerName;
        this.goodsName = goodsName;
    }

    /*调用从商店返回订购物品的信息*/
    public String orderGoods() {
        String goodsState = "--";
        MyRunnable mr = new MyRunnable();
        Thread t = new Thread(mr);
        t.start();
        System.out.println(goodsState);
        goodsState = mr.getResult();// 得到返回值
        myFeeling();// 用来测试异步是不是还是按顺序的执行
        return goodsState;
    }

    public void myFeeling() {
        String[] s = {"有点小激动", "很期待!", "希望是个好货!"};
        Random random = new Random();
        int temp = random.nextInt(3);
        System.out.println("我是" + this.getBuyerName() + ", 我现在的感觉: " + s[temp]);
    }

    /*被回调的方法, 我们自己不去调用, 这个方法给出的结果, 是其他接口或者程序给我们的, 我们自己无法产生*/
    @Override
    public String getOrderResult(String state) {
        return "在" + this.getStore().getName() + "商店订购的" + this.getGoodsName() + "玩具, 目前的预订状态是: " + state;
    }

    // 开启另一个线程, 但是没有返回值, 怎么回事
    // 调试的时候, 等待一会儿, 还是可以取到值, 但不是立即取到, 在print显示的时候, 却是null, 需要注意?
    private class MyRunnable implements Runnable {
        @Getter
        @Setter
        private String result;

        @Override
        public void run() {
            try {
                Thread.sleep(10000);
                result = store.returnOrderGoodsInfo(NoSyncBuyer.this);// 匿名函数的时候, 无法return 返回值
            } catch (InterruptedException e) {
                log.error("出大事了, 异步回调有问题了", e);
            }
        }
    }
}

Test2Callback类,测试同步回调和异步回调的结果。

public class Test2Callback {
    public static void main(String[] args) {
        Store wallMart = new Store("沙中路沃尔玛");
        SyncBuyer syncBuyer = new SyncBuyer(wallMart, "小明", "超能铁扇公主");
        System.out.println(syncBuyer.orderGoods());

        System.out.println("\n");
        Store lawson = new Store("沙中路罗森便利店");
        NoSyncBuyer noSyncBuyer = new NoSyncBuyer(lawson, "cherry", "变形金刚");
        System.out.println(noSyncBuyer.orderGoods());
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/55239.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【论文速读】Scene Text Telescope: Text-Focused Scene Image Super-Resolution

前言 在阅读这篇文章的时候,我花费了近一周的时间在将其基本消化理解,至于为什么花费如此长的时间,我发现主要原因是我对transformer一知半解,所以在transformer中提出的名词,例如:Postion-Wise Feed-Forw…

365天深度学习训练营-第P3周:天气识别

🍨 本文为🔗365天深度学习训练营 内部限免文章(版权归 K同学啊 所有)🍦 参考文章地址: 🔗第P3周:天气识别 | 365天深度学习训练营🍖 作者:K同学啊 | 接辅导、…

视频转文字怎么操作?这三种转换方法你该学会

如今短视频让各种知识传播变得生动形象,但是视频学习对于后期的整理复习不是很便捷,现在教大家一种好用的视频知识整理方法,那就是视频转文字,可以将视频内容轻松转换为文字形式。那么就有人问了,怎样转换才更简单呢&a…

WeakHashMap 和 HashMap 的区别是什么,何时使用?

本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问。 前言 大家好,我是小彭。 在之前的文章里,我们聊到了 Java 标准库中 HashMap 与 LinkedHashMap 的实现原理。HashMap 是一个标准的散列表数据结构&…

eval()方法字符串转对象; 分别取对象属性名和属性的方法

字符串转对象 ⬇️ A是字符串 转对象: let B eval(‘ (‘ A ‘) ‘) B就是A转成的对象 取对象属性名(for ... in ... ) ⬇️ let C []; for(let key in B) { C.push(key); console.log(key); // 打印所有属性名 console.log(B[key]); // 打印所有属性值…

“元宇宙”虚拟世界的营销法则 “品牌元宇宙空间”算什么?

虚拟世界从来不缺吃螃蟹的品牌。 “元宇宙”依旧是当下品牌创新营销的重要形式,从时趣的行业观察来看,大量品牌方都有着元宇宙的营销意向,但在营销落地上存在不同的进度。一个显而易见的事实是,元宇宙不仅仅是一个虚拟的游戏空间…

RabbitMQ之负载均衡-HAProxy

将客户端的连接和操作的压力分散到集群中的不同节点,防止单个或几台服务器压力过大成为访问的瓶颈,甚至宕机。HAProxy是一款开源免费,并提供高可用性、负载均衡以及基于TCP和HTTP协议的代理软件,可以支持四层、七层负载均衡&#…

虚拟列表渲染-前端性能优化

目录 1 使用场景 2 小插曲 3 虚拟列表渲染实现原理 4 代码实现 1 使用场景 在做EMBP项目时,有个模板新建的需求,需要点击获取子任务时获取所有的子任务,当时有的数据比较大,会有几百条的情况,此时接口请求变得很慢…

高可用网站架构云化

高可用网站架构云化 一、背景 早期互联网产品用户量少,并发量低,数据量小,多数只需要单个应用服务器可以满足需要,而数据库和文件服务部署在外部单个服务器上。随着业务在线化、互联网化的高速发展,企业对核心业务系统…

微信云开发AI短视频一键换脸小程序源码

简介: 微信云开发AI一键视频换脸小程序源码是由极客二改后发布的,小程序增加了广告控制, 插屏广告,激励广告和原生广告,由于采用了微信云开发没有后台, 所以不需要域名和服务器也可以正常搭建使用&#xf…

【论文精读6】MVSNet系列论文详解-CIDER

CIDER全名:Learning Inverse Depth Regression for Multi-View Stereo with Correlation Cost Volume,AAAI 2020(CCF A) 本文是MVSNet系列的第6篇,建议看过【论文精读1】MVSNet系列论文详解-MVSNet之后再看便于理解。 一、问题引入 针对问题&#xff1a…

理解 CNN

理解 CNN 注意:下面提到的图像指位图 目录理解 CNNCNN人类的视觉原理几个关键层卷积层(fliter、kernel)池化层 (pooling)激活层(activate)全连接层(Linear)pytorch实现TextCNN卷积传播图解不同视角看CNN参考CNN 卷积神经网络-CNN 最擅长的就是图片的处理。它受到人…

[附源码]JAVA毕业设计机房预约系统(系统+LW)

[附源码]JAVA毕业设计机房预约系统(系统LW) 目运行 环境项配置: Jdk1.8 Tomcat8.5 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术&…

Spring Data JPA 中的分页和排序

让我们学习如何使用 在 Spring 数据 JPA 中使用分页和排序有效地处理大量记录。 什么是分页和排序? 大多数情况下,来自数据库的结果集可能会让人不知所措。它可能会让人不知所措,以至于系统崩溃,因为它们无法在一次传递中处理那…

2019-10《信息资源管理 02378》真卷(独家文字版),圈定章节考点+统计真题分布

本系列博客合计 21 篇,每篇都将解析一张《信息资源管理》真卷,并附带答案解析与背诵技巧。 搜索 梦想橡皮擦,擦姐整理 全国 2019 年 10 月自学考试信息资源管理试题(02378) 单选题 1,信息技术是应用信息科学的原理和…

Linux基本工具——yum

Linux基本工具软件包管理器yum什么是软件包Linux的软件包yum的三板斧yum的扩展软件包管理器yum 工具的本质就是指令 什么是软件包 我们平时用手机与电脑的时候,如果想安装一个软件就需要去下载它的安装包(软件包),下载安装包的…

前端怎么才能找到项目做?

前言 一般找项目最常用的就是在Github上,题主已经具备了html、css、js和vue的知识体系,那么在github或者找一些项目教程视频来实践是锻炼的最好途径,下面我整理了一些在github和B站上找了一些前端的经典项目,希望对你有所帮助~ …

Kotlin 开发Android app(十六):ContentProvider的使用

android的四大组件,已经介绍了两个,这一节介绍ContentProvider。前面的广播可以进行 app内的通讯,如果需要进行app之间的通讯,在android 中使用的是ContentProvider。ContentProvider 也分为三种,一,作为数…

物通博联持续参与京东方(BOE)工厂数字化项目

创新引领 数字驱动 京东方科技集团股份有限公司(BOE)创立于1993年,是全球领先的半导体显示技术、产品与服务提供商,核心业务包括显示器件、智慧系统和健康服务。产品广泛应用于手机、平板电脑、笔记本电脑、显示器、电视、车载、…

php万年历源代码!源代码![上一年、上一月、下一月、下一年、附加当天日期加背景颜色]-私聊源码

//当前年 $year isset($_GET[year])?$_GET[year]:date(Y); //echo $year; //制作当前月的变量 $monthisset($_GET[month])?$_GET[month]:date(n); //本月有多少天 $day date(t,mktime(0,0,0,$month,1,$year)); //本月从1号开始是星期几 $w date(w,mktime(0,0,0,$month,1…