一文彻底搞懂Java中的回调函数

 更新时间:2026年01月20日 10:08:42   作者:马猴烧酒.  
回调是一种常见的编程模式,允许一个函数在特定事件或任务完成后,调用另一个预先定义的函数,这篇文章主要介绍了Java中回调函数的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

在学习代理设计模式中提到了“回调”这个东东,一下子把我打蒙了。于是决心花点小时间彻底理解java里的回调是什么。

是什么

定义

回调(Callback)是 Java 中最基础、最核心的编程设计思想,本质是「方法作为参数传递 + 控制权反转」的实现。回调逻辑的执行时机,由被调用方决定,而非调用方(控制权反转)

回调 = 调用方把「自己的逻辑(方法)」交给被调用方 → 被调用方在「指定时机」主动执行该逻辑

好比点外卖时(场景)我把我的电话号码(对象)交给店铺(被调用者),店铺会在我的外卖送到时(指定时机)给我打电话(对象可以调用的方法)。

成员:

心成员 1:「调用方(Caller)」

▸ 核心职责:定义回调逻辑(或持有已有类的回调方法) + 将回调载体(接口对象 / 普通类对象)传递给被调用方

▸ 核心特征:主动发起交互,把自己的逻辑交付出去,不控制回调执行时机。

 核心成员 2:「被调用方(Callee)」

▸ 核心职责:接收回调载体 + 执行自身核心业务 + 在指定时机主动调用回调载体中的方法

▸ 核心特征:拥有「回调触发权」,决定回调逻辑什么时候执行,是回调的主导方。

核心成员 3:「回调体(Callback Body)」

▸ 核心定义:封装了回调逻辑的载体(可以是「接口实现类对象」、也可以是「已有方法的普通类对象」),是调用方与被调用方的「通信桥梁」;

▸ 核心特征:内部包含具体的「回调方法」,供被调用方主动触发。

实例

同步回调(对应的异步会回调就是在被调用方内部开个独立线程)

// ========== 步骤1:定义回调接口(约定回调方法规范,核心) ==========
interface MyCallback {
    // 回调方法:被调用方在「任务完成后」主动调用
    void onTaskFinish(String result);
}

// ========== 步骤2:被调用方:接收回调对象,执行核心任务后触发回调 ==========
class CallbackTaskService {
    // 核心方法:入参为「回调接口对象」,接收调用方的逻辑
    public void doTask(MyCallback callback) {
        System.out.println("【被调用方】开始执行耗时核心任务...");
        // 模拟核心任务执行(如数据库查询、网络请求)
        String taskResult = "核心任务执行成功,返回结果";
        // 关键:被调用方主动触发回调(时机由被调用方决定)
        callback.onTaskFinish(taskResult);
    }
}

// ========== 步骤3:调用方:实现回调接口,传递回调对象 ==========
public class CallbackCallDemo {
    public static void main(String[] args) {
        CallbackTaskService service = new CallbackTaskService();
        
        // 调用方通过「匿名内部类」实现回调接口
        MyCallback callback = new MyCallback() {
            @Override
            public void onTaskFinish(String result) {
                // 调用方自定义的回调逻辑:处理被调用方返回的结果
                System.out.println("【调用方-回调逻辑】收到被调用方结果:" + result);
            }
        };
        
        // 传递回调对象给被调用方
        service.doTask(callback);
        // 核心优势:调用方无需等待,可立即执行自身其他逻辑
        System.out.println("【调用方】发起任务后,直接执行自身后续逻辑,无需等待任务完成");
    }
}

注意,这里不是必须得有个接口什么的,这不是一种固定的代码写法而是一种设计模式。只是使用接口可以实现强解耦,统一规范,是最佳实践。在实例代码的基础上还可以用lambda简化等等。

常见应用

像代理模式中就有用到,不过里面层层包装很难看清怎么用的,这里只讲讲线程 / 线程池回调。

// 回调体:Runnable是回调接口,匿名内部类实现的是「回调对象」
Runnable callbackObj = new Runnable() {
    @Override
    public void run() {
        // 👉 回调方法:存放「用户自定义的业务逻辑」(扩展逻辑)
        System.out.println("【线程回调】执行线程内业务逻辑");
    }
};
// 被调用方:Thread类 + JVM(核心逻辑执行者,掌握回调触发权)
Thread thread = new Thread(callbackObj);
// 调用方:我们的主线程代码(传递回调体、发起调用,不控制触发时机)
thread.start(); 

执行链路

1. 主线程调用 thread.start() → 向JVM发起「线程启动请求」,线程进入「就绪状态」;

2. JVM线程调度器分配CPU资源 → 线程从「就绪」转为「运行状态」;

3. JVM底层主动调用 Thread类中的run()方法;

4. Thread类的run()方法内部,会调用「传入的Runnable对象」的run()方法;

5.  最终触发:我们自定义的回调逻辑(Runnable匿名内部类的run())执行;

6. 业务逻辑执行完毕 → 线程进入「终止状态」,回调完成。

常见应用场景

应用领域具体场景核心回调接口 / 方法触发时机
JDK 原生线程 / 线程池Runnable.run()/Callable.call()线程启动后
JDK 原生异步回调CompletableFuture.thenAccept()异步任务完成后
JDK 原生集合遍历Collection.forEach()遍历每个元素时
Spring 生态Bean 生命周期InitializingBean.afterPropertiesSet()Bean 初始化完成后
Spring 生态事件监听ApplicationListener.onApplicationEvent()发布事件时
Spring 生态MVC 拦截器HandlerInterceptor.preHandle()请求处理前后
动态代理CGLIB/JDK 代理MethodInterceptor.intercept()/InvocationHandler.invoke()调用代理方法时
MyBatis类型转换TypeHandler.setNonNullParameter()参数设置 / 结果映射时
NettyNIO 事件ChannelHandler.channelRead()IO 事件触发时
定时任务定时器TimerTask.run()定时时间到达时

为什么

其实跟大多数设计模式一样,还是「传递逻辑、移交控制、分离职责」这些优点,帮助我们写出逻辑性更强更简约的代码。

不过这里的什么解决代码紧耦合,设计模式里的装饰器模式,观察者模式等等都能做到。代码量太低的我就很难理解里面的精妙之处,特别之处了,这里更多的是记住“这种场景”来选择使用。

总结

到此这篇关于Java中回调函数的文章就介绍到这了,更多相关Java回调函数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java后台接收app上传的图片的示例代码

    java后台接收app上传的图片的示例代码

    本篇文章主要介绍了java后台接受app上传的图片的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • springboot解决前后端分离时的跨域问题

    springboot解决前后端分离时的跨域问题

    这篇文章主要介绍了springboot如何解决前后端分离时的跨域问题,帮助大家更好的理解和学习使用springboot,感兴趣的朋友可以了解下
    2021-04-04
  • SpringBoot项目如何打包部署到服务器

    SpringBoot项目如何打包部署到服务器

    这篇文章主要介绍了SpringBoot项目如何打包部署到服务器问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-03-03
  • Mybatis中@Param注解的用法详解

    Mybatis中@Param注解的用法详解

    @Param注解的作用是给参数命名,参数命名后就能根据名字得到参数值,正确的将参数传入sql语句中,下面这篇文章主要给大家介绍了关于Mybatis中@Param注解用法的相关资料,需要的朋友可以参考下
    2022-07-07
  • Springboot2 配置AOP日志的方法步骤

    Springboot2 配置AOP日志的方法步骤

    这篇文章主要介绍了Springboot2 配置AOP日志的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06
  • 使用IntelliJ IDEA调式Stream流的方法步骤

    使用IntelliJ IDEA调式Stream流的方法步骤

    本文主要介绍了使用IntelliJ IDEA调式Stream流的方法步骤,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • Java线程中卖火车票问题的深入讲解

    Java线程中卖火车票问题的深入讲解

    这篇文章主要给大家介绍了关于Java线程中卖火车票问题的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • Spring Bean初始化及@PostConstruc执行顺序示例详解

    Spring Bean初始化及@PostConstruc执行顺序示例详解

    本文给大家介绍Spring Bean初始化及@PostConstruc执行顺序,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2025-07-07
  • 关于Springboot | @RequestBody 接收到的参数对象属性为空的问题

    关于Springboot | @RequestBody 接收到的参数对象属性为空的问题

    这篇文章主要介绍了关于Springboot | @RequestBody 接收到的参数对象属性为空的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-03-03
  • 如何基于Jenkins构建Docker镜像

    如何基于Jenkins构建Docker镜像

    这篇文章主要介绍了基于Jenkins构建Docker镜像,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11

最新评论