【无标题】

详解Java的回调机制

 更新时间:2022年05月20日 09:26:38   作者:Q-WHai  
最近学习java,接触到了回调机制(CallBack)。初识时感觉比较混乱,而且在网上搜索到的相关的讲解,本文介绍了Java的回调机制,有兴趣的同学可以了解一下

模块之间总是存在这一定的接口,从调用方式上看,可以分为三类:同步调用、回调和异步调用。下面着重详解回调机制。

1. 概述

Java 中的回调机制是一个比较常见的机制,只是有可能在你的程序中使用得比较少,在一些大型的框架中回调机制随处可见。本文就通过一些具体的实例,慢慢走近 Java 的回调机制。

2.回调

所谓回调:就是A类中调用B类中的某个方法C,然后B类中反过来调用A类中的方法D,D这个方法就叫回调方法。实际在使用的时候,也会有不同的回调形式,比如下面的这几种。

2.1 同步回调

这里我假设这样的一种情况。

A 公司的总监 B 跟他的下属(项目经理 C)说要做一个调研,不过不用 C 自己亲力亲为。可以让经理 C 去安排他下面的程序员 D 去完成。经理 C 找到了程序员 D,并告诉他,现在要完成一个调研任务。并且把调研的结果告诉经理 C。如果有问题,还是要继续的。 因为这里是 C 让 D 去做一件事情,之后 D 还是要将结果与 C 进行沟通。这样就是回调的模型了。下面是一般回调的类图:

首先我们要有一个回调的接口 CallbackInterface

CallbackInterface.java

?
1
2
3
public interface CallbackInterface {
   public boolean check( int result);
}

背景里,程序员 D 是要将结果与项目经理 C 进行沟通的,所以这里项目经理需要实现上面的回调接口:

Manager.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Manager implements CallbackInterface {
 
   private Programmer programmer = null ;
 
   public Manager(Programmer _programmer) {
     this .programmer = _programmer;
   }
 
   /**
    * 用于 Boss 下达的委托
    */
   public void entrust() {
     arrange();
   }
 
   // 进行安排下属进行 study 工作
   private void arrange() {
     System.out.println( "Manager 正在为 Programmer 安排工作" );
     programmer.study(Manager. this );
     System.out.println( "为 Programmer 安排工作已经完成,Manager 做其他的事情去了。" );
   }
 
   @Override
   public boolean check( int result) {
     if (result == 5 ) {
       return true ;
     }
     return false ;
   }
 
}

对于程序员 D 来说他需要持有一个经理 C 的引用,以便与他沟通。不过,这里是总监 B 让 经理 C 去安排的任务。也就是说这里也可以让其他的经理,比如说经理 B1, B2等等。因为经理都实现了回调的接口,所以这里就可以直接让程序员 D 持有这个接口就可以了。如下:

Programmer.java

?
1
2
3
4
5
6
7
8
9
10
11
12
public class Programmer {
 
   public void study(CallbackInterface callback) {
     int result = 0 ;
     do {
       result++;
       System.out.println( "第 " + result + " 次研究的结果" );
     } while (!callback.check(result));
 
     System.out.println( "调研任务结束" );
   }
}

对于总监来说就更简单明了了,因为这相当于一个 Client 测试:

Boss.java

?
1
2
3
4
5
6
7
public class Boss {
 
   public static void main(String[] args) {
     Manager manager = new Manager( new Programmer());
     manager.entrust();
   }
}

运行结果:

Manager 正在为 Programmer 安排工作
第 1 次研究的结果
第 2 次研究的结果
第 3 次研究的结果
第 4 次研究的结果
第 5 次研究的结果
调研任务结束
为 Programmer 安排工作已经完成,Manager 做其他的事情去了。

2.2 异步回调

还是上面的例子,你的项目经理不可能要一直等你调研的结果。而是把这个任务交给你之后,他就不管了,他做他的,你做你的。所以,这里需要对回调的函数进行异步处理。
所以,这里我们需要修改 Programmer 类的代码,修改如下:

Programmer.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Programmer {
 
   public Programmer() {
   }
 
   public void study(CallbackInterface callback) {
     new StudyThread(callback).start();
   }
 
   // --------------------------- Programmer 正在做的工作 ---------------------------
 
   class StudyThread extends Thread {
 
     CallbackInterface callback = null ;
 
     public StudyThread(CallbackInterface _callback) {
       callback = _callback;
     }
 
     @Override
     public void run() {
       int result = 0 ;
       do {
         result++;
         System.out.println( "第 " + result + " 次研究的结果" );
       } while (!callback.check(result));
 
       System.out.println( "调研任务结束" );
     }
   }
}

运行结果:

Manager 正在为 Programmer 安排工作
为 Programmer 安排工作已经完成,Manager 做其他的事情去了。
第 1 次研究的结果
第 2 次研究的结果
第 3 次研究的结果
第 4 次研究的结果
第 5 次研究的结果
调研任务结束

2.3 闭包与回调

闭包(closure)是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。

2.3.1 普通调用

首先,我们可以看看在正常情况下的调用是怎么进行的。
Incrementable.java

?
1
2
3
interface Incrementable {
   void increment();
}

这是一个普通的接口(在普通调用里只是普通接口,在回调中就是回调接口,这一点应该很好理解吧)。

Callee1.java

?
1
2
3
4
5
6
7
8
9
10
11
class Callee1 implements Incrementable {
 
   private int i = 0 ;
 
   @Override
   public void increment() {
     i++;
     System.out.println(i);
   }
 
}

Callbacks.java

?
1
2
3
4
5
6
public class Callbacks {
   public static void main(String[] args) {
     Callee1 callee1 = new Callee1();
     callee1.increment();
   }
}

Callbacks 是一个测试客户端类,没啥好说的,直接看上面的代码。

2.3.2 回调初试

上面的普通调用也没啥好说的,因为这对于一个正常的 Java 程序员来说都应该是想都不用想就可以搞定的事情。

现在如果要构成回调,那么对于程序的结构或是逻辑的思维上都不可能只有一个被调用者(被回调的对象 Callee1),还需要一个调用者对象。调用者可以像下面这样来编写:

Caller.java

?
1
2
3
4
5
6
7
8
9
10
11
12
class Caller {
 
   private Incrementable callbackReference;
 
   public Caller(Incrementable _callbackReference) {
     callbackReference = _callbackReference;
   }
 
   void go() {
     callbackReference.increment();
   }
}

这里 Caller 持有一个回调接口的引用 callbackReference,就像在上面说到的程序员需要持有一个项目经理的引用,这样就可以通过这个引用来与项目经理沟通。这里的 callbackReference 也正是起到了这个作用。

现在我们来看看测试类的编写:

Callbacks.java

?
1
2
3
4
5
6
7
public class Callbacks {
   public static void main(String[] args) {
     Callee1 callee1 = new Callee1();   
     Caller caller1 = new Caller(callee1);
     caller1.go();
   }
}

对于到目前为止的程序代码,完全可以对比上面项目经理安排程序员调研技术难题的代码。有异曲同工之妙。

2.3.3 闭包回调

相比于正常的回调,闭包回调的核心自然是在于闭包,也就是对作用域的控制。
现在假设有一个用户(其他程序员)自定义了一个 MyInCrement 类,同时包含了一个 increment 的方法。如下:

?
1
2
3
4
5
6
7
8
9
10
class MyInCrement {
 
   public void increment() {
     System.out.println( "MyCrement.increment" );
   }
 
   static void f(MyInCrement increment) {
     increment.increment();
   }
}

另外有一个类 Callee2 继承自上面这个类:

?
1
2
3
4
5
6
7
8
9
10
class Callee2 extends MyInCrement {
 
   private int i = 0 ;
 
   public void increment() {
     super .increment();
     i++;
     System.out.println(i);
   }
}

显而易见这里如果要调用 increment() 方法,就变成了一般的函数调用了。所以这里我们需要修改上面的 Callee2 类,修改的目标就是让 Callee2 类可以兼容 MyInCrement 类的 increment() 方法和 Incrementable 的 increment() 方法。修改后:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Callee2 extends MyInCrement {
 
   private int i = 0 ;
 
   public void increment() {
     super .increment();
     i++;
     System.out.println(i);
   }
 
   private class Closure implements Incrementable {
 
     @Override
     public void increment() {
       Callee2. this .increment();
     }
   }
 
   Incrementable getCallbackReference() {
     return new Closure();
   }
}

注意,这里的 Closure 类是一个私有的类,这是一个闭包的要素。因为 Closure 类是私有的,那么就要有一个对外开放的接口,用来对 Closure 对象的操作,这里就是上面的 getCallbackReference() 方法。 Caller 类则没有改变。
对于测试客户端就直接看代码吧:

?
1
2
3
4
5
6
7
public class Callbacks {
   public static void main(String[] args) {   
     Callee2 callee2 = new Callee2();
     Caller caller2 = new Caller(callee2.getCallbackReference());
     caller2.go();
   }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

                        <div class="art_xg">
                            <b>您可能感兴趣的文章:</b><ul><li><a href="/article/192275.htm" title="java调用回调机制详解" target="_blank">java调用回调机制详解</a></li><li><a href="/article/174845.htm" title="通过简易例子讲解Java回调机制" target="_blank">通过简易例子讲解Java回调机制</a></li><li><a href="/article/167197.htm" title="Java的回调机制实例详解" target="_blank">Java的回调机制实例详解</a></li><li><a href="/article/162064.htm" title="深入了解Java接口回调机制" target="_blank">深入了解Java接口回调机制</a></li><li><a href="/article/158806.htm" title="详解java 三种调用机制(同步、回调、异步)" target="_blank">详解java 三种调用机制(同步、回调、异步)</a></li><li><a href="/article/124029.htm" title="java 回调机制的实例详解" target="_blank">java 回调机制的实例详解</a></li><li><a href="/article/112637.htm" title="java回调机制实例详解" target="_blank">java回调机制实例详解</a></li><li><a href="/article/106589.htm" title="Java回调机制解读" target="_blank">Java回调机制解读</a></li><li><a href="/article/106181.htm" title="Java 异步回调机制实例分析" target="_blank">Java 异步回调机制实例分析</a></li><li><a href="/article/105016.htm" title="Java 回调机制(CallBack) 详解及实例代码" target="_blank">Java 回调机制(CallBack) 详解及实例代码</a></li><li><a href="/article/205646.htm" title="两个例子了解java中的回调机制" target="_blank">两个例子了解java中的回调机制</a></li></ul>
                        </div>

                    </div>
                    <!--endmain-->
                    <div class="lbd_bot clearfix"><script async="" src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script><ins class="adsbygoogle" style="display:block" data-ad-client="ca-pub-6384567588307613" data-ad-slot="6445926239" data-ad-format="auto" data-full-width-responsive="true"></ins><script>(adsbygoogle = window.adsbygoogle || []).push({});</script></div><div id="ewm"><div class="jb51ewm"><div class="fl"><img src="//files.jb51.net/skin/2018/images/jb51ewm.png"></div><div class="fr"><p>微信公众号搜索 “ <span>脚本之家</span> ” ,选择关注</p><p>程序猿的那些事、送书等活动等着你</p></div></div></div>
                    <div class="tags clearfix">
                        <i class="icon-tag"></i>
                        <ul class="meta-tags">
                            <li class="tag item"><a href="http://common.jb51.net/tag/java/1.htm" target="_blank" title="搜索关于java的文章" rel="nofollow">java</a></li>
  • 回调机制
  •                         </ul>
                        </div>
                        <div class="lbd clearfix">
                            <span id="art_down" class="jbTestPos"><div class="_9kdrht8lt5i"></div></span>
                        </div>
                        <div id="shoucang"></div>
                        <div class="xgcomm clearfix">
                            <h2>相关文章</h2>
                            <ul><li class="lbd clearfix"><span id="art_xg" class="jbTestPos"><div class="_xfzpeb4v72k"></div></span></li><li><div class="item-inner"><a href="/article/214024.htm" title="IDEA中JetBrains Mono字体的正确安装姿势" class="img-wrap" target="_blank"> <img alt="IDEA中JetBrains Mono字体的正确安装姿势" src="//img.jbzj.com/images/xgimg/bcimg0.png"> </a><div class="rbox"><div class="rbox-inner"><p><a class="link title" target="_blank" href="/article/214024.htm" title="IDEA中JetBrains Mono字体的正确安装姿势">IDEA中JetBrains Mono字体的正确安装姿势</a></p><div class="item-info"><div class="js">在 JetBrains Mono 的设计阶段,它就充分考虑到了长时间工作可能导致的眼睛疲劳问题,比如字母的大小和形状、空间量、自然等宽平衡、不必要的细节、连字、以及难以区分的符号等,从而最终设计出了这么一款字体</div><span class="lbtn" style="float:right"> 2021-06-06 </span></div></div></div></div></li>
    
  • java学生管理系统界面简单实现(全)

    java学生管理系统界面简单实现(全)

    这篇文章主要为大家详细介绍了java学生管理系统界面的简单实现,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01
  • java排查一个线上死循环cpu暴涨的过程分析

    java排查一个线上死循环cpu暴涨的过程分析

    这篇文章主要介绍了java排查一个线上死循环cpu暴涨的过程分析,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-08-08
  • spring cloud之eureka高可用集群和服务分区解析

    spring cloud之eureka高可用集群和服务分区解析

    这篇文章主要介绍了spring cloud之eureka高可用集群和服务分区解析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • Spring实现邮件发送功能

    Spring实现邮件发送功能

    这篇文章主要为大家详细介绍了Spring实现邮件发送功能,简单的发送邮件工具JavaMailSender使用,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-11-11
  • 15道非常经典的Java面试题 附详细答案

    15道非常经典的Java面试题 附详细答案

    这篇文章主要为大家推荐了15道非常经典的Java面试题,附详细答案,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-10-10
  • MongoDB整合Spring实例详细讲解(含代码)

    MongoDB整合Spring实例详细讲解(含代码)

    这篇文章主要介绍了MongoDB整合Spring实例详细讲解(含代码),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01
  • Java实现的分页工具类与用法示例

    Java实现的分页工具类与用法示例

    这篇文章主要介绍了Java实现的分页工具类与用法,结合完整实例形式分析了java分页工具类的定义、使用方法及相关操作技巧,需要的朋友可以参考下
    2019-10-10
  • 详解SpringMVC拦截器(资源和权限管理)

    详解SpringMVC拦截器(资源和权限管理)

    本篇文章主要介绍了SpringMVC拦截器(资源和权限管理),具有一定的参考价值,有兴趣的可以了解一下。
    2016-12-12
  • Java多线程yield心得分享

    Java多线程yield心得分享

    前几天复习了一下多线程,发现有许多网上讲的都很抽象,所以,自己把网上的一些案例总结了一下
    2013-12-12
  • 最新评论

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值