详解Java的回调机制_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

public interface CallbackInterface {
  public boolean check(int result);
}

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

Manager.java

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

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

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

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

interface Incrementable {
  void increment();
}

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

Callee1.java

class Callee1 implements Incrementable {

  private int i = 0;

  @Override
  public void increment() {
    i++;
    System.out.println(i);
  }

}

Callbacks.java

public class Callbacks {
  public static void main(String[] args) {
    Callee1 callee1 = new Callee1();
    callee1.increment();
  }
}

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

2.3.2 回调初试

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

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

Caller.java

class Caller {

  private Incrementable callbackReference;

  public Caller(Incrementable _callbackReference) {
    callbackReference = _callbackReference;
  }

  void go() {
    callbackReference.increment();
  }
}

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

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

Callbacks.java

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 的方法。如下:

class MyInCrement {

  public void increment() {
    System.out.println("MyCrement.increment");
  }

  static void f(MyInCrement increment) {
    increment.increment();
  }
}

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

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() 方法。修改后:

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 类则没有改变。
对于测试客户端就直接看代码吧:

public class Callbacks {
  public static void main(String[] args) {
    Callee2 callee2 = new Callee2();
    Caller caller2 = new Caller(callee2.getCallbackReference());
    caller2.go();
  }
}

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

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索java回调机制
, java回调机制过程
java异步回调
java回调机制详解、java 回调机制、java接口回调详解、java的回调机制、java中的回调机制,以便于您获取更多的相关知识。

时间: 2024-09-30 13:41:24

详解Java的回调机制_java的相关文章

详解 JAVA的回调机制CallBack_java

序言 CallBack是回调的意思,熟悉Windows编程的人对"回调函数"这四个字一定不会陌生,但是Java程序员对它可能就不太了解了."回调函数"或者"回调方法"是软件设计与开发中一个非常重要的概念,掌握"回调函数"的思想对程序员来说(不管用哪种语言)是非常必要的. 最近学习java,接触到了回调机制(CallBack).初识时感觉比较混乱,而且在网上搜索到的相关的讲解,要么一言带过,要么说的比较单纯的像是给CallBac

图文详解java内存回收机制_java

在Java中,它的内存管理包括两方面:内存分配(创建Java对象的时候)和内存回收,这两方面工作都是由JVM自动完成的,降低了Java程序员的学习难度,避免了像C/C++直接操作内存的危险.但是,也正因为内存管理完全由JVM负责,所以也使Java很多程序员不再关心内存分配,导致很多程序低效,耗内存.因此就有了Java程序员到最后应该去了解JVM,才能写出更高效,充分利用有限的内存的程序.  1.Java在内存中的状态  首先我们先写一个代码为例子: Person.java package tes

【Redis缓存机制】详解Java连接Redis_Jedis_事务_java

Jedis事务 我们使用JDBC连接Mysql的时候,每次执行sql语句之前,都需要开启事务:在MyBatis中,也需要使用openSession()来获取session事务对象,来进行sql执行.查询等操作.当我们对数据库的操作结束的时候,是事务对象负责关闭数据库连接. 事务对象用于管理.执行各种数据库操作的动作.它能够开启和关闭数据库连接,执行sql语句,回滚错误的操作. 我们的Redis也有事务管理对象,其位于redis.clients.jedis.Transaction下. Jedis事

详解java动态代理模式_java

本文针对java动态代理进行知识点整理,具体内容如下 一. JAVA的动态代理(比较官方说法) 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处 理消息.过滤消息.把消息转发给委托类,以及事后处理消息等. 代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的 对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提 供特定的服务. 按照代理的创建时期,代理类可以分为两种. 静态代理:由程序员创建或特定工

详解Java中native关键字_java

 一. 什么是Native Method   简单地讲,一个Native Method就是一个java调用非java代码的接口.一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C.这个特征并非java所特有,很多其它的编程语言都有这一机制,比如在C++中,你可以用extern "C"告知C++编译器去调用一个C的函数.    "A native method is a Java method whose implementatio

详解Java的堆内存与栈内存的存储机制_java

堆与内存优化    今天测了一个项目的数据自动整理功能,对数据库中几万条记录及图片进行整理操作,运行接近到最后,爆出了java.lang.outOfMemoryError,java heap space方面的错误,以前写程序很少遇到这种内存上的错误,因为java有垃圾回收器机制,就一直没太关注.今天上网找了点资料,在此基础上做了个整理.  一.堆和栈     堆-用new建立,垃圾回收器负责回收          1.程序开始运行时,JVM从OS获取一些内存,部分是堆内存.堆内存通常在存储地址的

详解Java的Spring框架中的事务管理方式_java

数据库事务是被当作单个工作单元的操作序列.这些操作要么全部完成或全部不成功.事务管理是面向企业应用程序,以确保数据的完整性和一致性RDBMS中的重要组成部分.事务的概念可以用下面的描述为ACID四个关键属性来描述: 原子性: 一个事务应该被视为单个操作单元表示的操作的任一整个序列是成功的或不成功的. 一致性: 这代表了数据库的参照完整性,在桌等唯一主键的一致性 隔离性: 可能有很多事务处理相同的数据集的同时,每个事务都应由他人隔离,以防止数据损坏. 持久性: 一旦事务完成,本次事务的结果必须作出

java 多态性详解及常见面试题_java

java多态性 多态分两种: (1)   编译时多态(设计时多态):方法重载. (2)   运行时多态:JAVA运行时系统根据调用该方法的实例的类型来决定选择调用哪个方法则被称为运行时多态.(我们平时说得多的事运行时多态,所以多态主要也是指运行时多态) 运行时多态存在的三个必要条件: 一.要有继承(包括接口的实现): 二.要有重写: 三.父类引用指向子类对象. 多态的好处: 1.可替换性(substitutability).多态对已存在代码具有可替换性.例如,多态对圆Circle类工作,对其他任

详解Java中的指针、引用及对象的clone

对象|详解 Java语言的一个优点就是取消了指针的概念,但也导致了许多程序员在编程中常常忽略了对象与引用的区别,本文会试图澄清这一概念.并且由于Java不能通过简单的赋值来解决对象复制的问题,在开发过程中,也常常要要应用clone()方法来复制对象.本文会让你了解什么是影子clone与深度clone,认识它们的区别.优点及缺点.看到这个标题,是不是有点困惑:Java语言明确说明取消了指针,因为指针往往是在带来方便的同时也是导致代码不安全的根源,同时也会使程序的变得非常复杂难以理解,滥用指针写成的