Java中使用回调函数的方法实例
作者:BillDavidup 发布时间:2023-01-28 19:24:21
背景
在Java中一个回调的操作是一个在一些操作完成之后被传递到另一个函数中并且被执行的函数。一个回调函数既可以被同步或者异步执行。在一个同步回调函数的案例中,一个函数紧着着另一个函数完成后被执行。在一个异步回调函数的案例中,一个函数不需要在其他函数执行的过程中按照特定顺序时间内被执行。
从在经典的监视者设计模式中使用的监听案例开始,这篇文章向你介绍了Java中的回调函数。你将会看到大量的同步和异步的回调实现,包含使用 CompletableFuture类的函数式回调。
Java中的同步回调
一个同步回调函数总是会在一些操作执行后立刻被执行。这意味着它将会在动作被执行后执行。
正如我提到的,在一个监视者设计模式中使用的一个回调函数的案例。在页面UI的按钮中,需要一个按钮被点击后去实例化一个操作,我们可以传递回调函数作为这个按钮动作的 * 。这个监听函数会一直等待按钮被点击,直到按钮被触发后执行。
现在让我们看代码中一个新的回调概念的案例
匿名内部类中的回调
任何时间我们在Java中,传递一个实现了接口的方法到另一个方法中,我们就是使用了回调函数的概念。在下面的代码中,我们传递了一个Consumer函数接口和一个匿名内部类去实现accept()方法。
一旦 accept() 方法被实现,我们将会在performAction方法里面执行它;然后我们会在Consumer接口里面执行accept() 方法。
import java.util.function.Consumer;
public class AnonymousClassCallback {
public static void main(String[] args) {
performAction(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
}
public static void performAction(Consumer<String> consumer) {
System.out.println("Action is being performed...");
consumer.accept("Callback is executed");
}
}
该代码的输出语句是:
Action is being performed…
Callback is executed…
在以上代码中,我们将Consumer接口传递到performAction() 方法中,然后performAction()方法执行后,调用accept()方法。
你可能注意到,使用一个匿名内部类是相当的啰嗦。使用lambda表达式替代匿名内部类将会更简单。让我们看看当我们在回调函数中使用lambda表达式会发生什么。
lambda的回调
在Java中,我们可以传递使用了lambda表达式实现的函数式接口到一个方法中,然后在一个操作结束后被执行。在代码中看起开是那样。
public class LambdaCallback {
public static void main(String[] args) {
performAction(() -> System.out.println("Callback function executed..."));
}
public static void performAction(Runnable runnable) {
System.out.println("Action is being performed...");
runnable.run();
}
}
输出结果表明,一旦这个操作执行后,回调即被调用。
在这个例子中,你可能注意到我们在performAction方法中传递了可运行的函数式接口。因此,我们可以重写run()方法并在performAction方法结束后执行run()方法。
异步回调函数
我们经常想去使用一个异步回调方法,这意味着一个方法将会在操作执行时被其他进程异步地调用。当一个回调方法不需要被其他进程立刻调用的时候,它可能对性能有所帮助。
简单线程回调
让我们以最简单的异步回调操作方式开始。在下面的代码中,首先我们从一个可执行的函数式接口中实现了run()方法。然后我们在一个创建线程中使用了它。最终,我们将会启动这个线程去异步地执行这个已实现的可执行函数式接口:
public class AsynchronousCallback {
public static void main(String[] args) {
Runnable runnable = () -> System.out.println("Callback executed...");
AsynchronousCallback asynchronousCallback = new AsynchronousCallback();
asynchronousCallback.performAsynchronousAction(runnable);
}
public void performAsynchronousAction(Runnable runnable) {
new Thread(() -> {
System.out.println("Processing Asynchronous Task...");
runnable.run();
}).start();
}
}
这个案例的输出结果是:
Processing Asynchronous Task…
Callback executed…
从上面的代码中,我们创建了一个实现run()方法的Runnable,然后我们调用performAsynchronousAction()方法,传递了Runnable,在新建的线程中run()方法调用了它。
在performAsynchronousAction() 方法内,我们向它传递了一个runnable参数,在方法内部用lambda实现了一个线程的run方法调用了它.。先打印 “Processing Asynchronous Task…” 最终方法内部调用了以参数传进来的runnable方法,打印 “Callback executed…”
平行执行的异步回调
比起在异步调用中回调函数,我们也可以从其他函数中平行地调用一个回调函数。这意味着我们可以开启两个线程,平行地调用这两个函数。
以下代码和之前的有点像,但是要注意到除了直接调用回调函数外,我们可以开启一个新的线程调用回调函数:
// Omitted code from above…
public void performAsynchronousAction(Runnable runnable) {
new Thread(() -> {
System.out.println("Processing Asynchronous Task...");
new Thread(runnable).start();
}).start();
}
输出结果是:
Processing Asynchronous Task…
Callback executed…
当我们不需要回调函数在performAsynchronousAction() 方法调用之后被立刻执行,异步平行调用回调函数是非常有用的。
一个真实世界的案例,当我们在线上购买一个产品,我们不需要等待付款被确认后才执行检查库存等所有的这些重度操作。在这个案例中,回调在后台执行的同时我们可以做其他事情。
CompletableFuture中的回调
使用异步回调函数的另一种方法是使用CompletableFuture API。Java8中引入的这个强大的API有助于执行和组合异步方法调用。它完成了我们在上一个示例中所做的一切,例如创建一个新的线程,然后启动和管理它。
在下面的代码示例中,我们将创建一个新的CompletableFuture,然后调用传递String的supplyAsync方法。
接下来,我们将创建另一个CompletableFuture,它将应用回调函数来执行我们配置的第一个函数:
import java.util.concurrent.CompletableFuture;
public class CompletableFutureCallback {
public static void main(String[] args) throws Exception {
CompletableFuture<String> completableFuture
= CompletableFuture.supplyAsync(() -> "Supply Async...");
CompletableFuture<String> execution = completableFuture
.thenApply(s -> s + " Callback executed...");
System.out.println(execution.get());
}
}
它的输出结果如下:
Supply Async… Callback executed…
结论
回调在软件开发中到处使用,广泛地使用在工具、设计模式和应用程序中。有时候我们使用它们的时候却没有注意到。
在java代码中,我们通过大量常见的回调实现帮助我们解释它们广泛的用处。这里有一些回调的特征需要我们记住:
回调函数应该在执行另一个操作时执行,或者与该操作并行执行。
回调函数可以被同步执行,意味着它可以在其他操作执行后没有延迟地被立刻执行。
回调函数可以是异步的,这意味着它可以在后台执行,并且可能需要一些时间才能执行
Observable设计模式使用回调来通知感兴趣的实体何时发生了操作。
来源:https://blog.csdn.net/qq_21922755/article/details/130837048
猜你喜欢
- 堆区:只存放类对象,线程共享;方法区:又叫静态存储区,存放class文件和静态数据,线程共享;栈区:存放方法局部变量,基本类型变量区、执行环
- 本文实例为大家分享了java抓取邮箱号码的具体代码,供大家参考,具体内容如下java抓取文件中邮箱号码的具体代码package reg;im
- SSM在Controller中添加事务管理本人使用:集成开发环境:idea项目管理工具:maven数据库:oracle框架:Spring+S
- 前言前一段时间使用java来调用chatgpt的接口,然后写了一个简单小程序,java调用chatgpt接口,实现专属于自己的人工智能助手,
- C#与C++ dll之间传递字符串string wchar_t* char* IntPtr1、由C#向C++ dll 传入字符串时,参数直接
- 前言之前一篇文章介绍了基本的统一异常处理思路: Spring MVC/Boot 统一异常处理最佳实践.上篇文章也有许多人提出了一些问题:如何
- MyBatis框架提供了二级缓存接口,我们只需要实现它再开启配置就可以使用了。特别注意,我们要解决缓存穿透、缓存穿透和缓存雪崩的问题,同时也
- 前言Mybatis作为一个应用广泛的优秀的ORM框架,已经成了JavaWeb世界近乎标配的部分,这个框架具有强大的灵活性,在四大组件(Exe
- jmap命令可以打印java进程的JVM堆信息,今天在某台机器上运行该命令查看 19560进程的堆信息jmap -heap 19560出现以
- 一. String类简介1. 介绍字符串广泛应用 在 Java 编程中,在 Java 中字符串属于对象,Java 提供了 String 类来
- 本文以实例形式详细讲述了Java的反射机制,是Java程序设计中重要的技巧。分享给大家供大家参考。具体分析如下:首先,Reflection是
- 方式1:1. 明确 Spark中Job 与 Streaming中 Job 的区别1.1 Spark Core一个 RDD DAG Graph
- 定义:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。类型:行为类
- 第一部分:Java数据结构要理解Java数据结构,必须能清楚何为数据结构?数据结构:Data_Structure,它是储存数据的一种结构体,
- 平时用到的库仓库名地址备注mavenCentralhttps://repo1.maven.org/maven2/
- MD5加密简介哈希算法又称散列算法,是将任何数据转换成固定长度的算法的统称。 从本质上讲,MD5也是一种哈希算法,其输出是生成12
- 系统参数系统级全局变量,该参数在程序中任何位置都可以访问到。优先级最高,覆盖程序中同名配置。系统参数的标准格式为:-Dargname=arg
- 在项目中如果有些参数经常需要修改,或者后期可能需要修改,那我们最好把这些参数放到properties文件中,源代码中读取properties
- 这篇文章主要介绍了简单了解Java多态向上转型相关原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋
- 使用ProcessBuilder踩到的坑最近使用ProcessBuilder执行命令,命令内容正确,但始终报错命令实行失败,是因为不熟悉Pr