当前位置: 首页 > news >正文

深入理解Java函数式编程:Supplier与延迟创建对象实战

目录

一.Supplier接口的核心概念与语法基础

1. 什么是Supplier?

2. 基础热身:感受延迟创建的威力

二.揭秘双冒号(::)构造器引用

1. 为什么可以这么写?

2. Lambda与双冒号的等价转换

三.延迟创建对象在实际场景中的巨大好处

1. 模拟重量级资源的按需加载

2. 带来的实际收益

四.避坑指南:警惕频繁触发的副作用


在Java 8引入Lambda表达式和函数式接口后,我们的代码编写方式发生了巨大的改变。在日常开发中,我们可能会遇到诸如Supplier<Customer> customerSupplier = Customer::new;这样看似“奇特”的写法。这其实涉及到了Java中的延迟初始化(Lazy Initialization)思想。本文将带你通过实战代码,彻底搞懂这一套机制。

一.Supplier接口的核心概念与语法基础

1. 什么是Supplier?

java.util.function包下,Supplier<T>是一个标准的函数式接口。它的核心特征是:无参数输入,有返回值输出。你可以把它理解为一个“工厂”或者一个“待命的动作”,它封装了创建某个对象的逻辑,但不会立即执行。

2. 基础热身:感受延迟创建的威力

为了直观感受传统写法和Supplier的区别,我们先看一段对比代码

import java.util.function.Supplier; class Customer { private String name; public Customer() { this.name = "默认客户"; System.out.println("[Customer] 构造方法被调用,对象已创建!"); } } public class Main { public static void main(String[] args) { // 1. 传统写法:立刻创建对象 System.out.println("--- 传统写法 ---"); Customer c1 = new Customer(); // 2. Supplier 写法:只是把“创建动作”打包起来,此时并没有执行 System.out.println("--- Supplier 写法 ---"); Supplier<Customer> customerSupplier = Customer::new; // 3. 只有真正调用 get() 的时候,才会去创建对象 System.out.println("--- 调用get()方法,才真正创建Customer对象 ---"); Customer c2 = customerSupplier.get(); } }

运行结果:

--- 传统写法 --- [Customer] 构造方法被调用,对象已创建! --- Supplier 写法 --- --- 调用get()方法,才真正创建Customer对象 --- [Customer] 构造方法被调用,对象已创建!

观察点:运行这段代码你会发现,打印的顺序完美印证了“延迟创建”。当你定义customerSupplier时,控制台没有任何输出;直到调用了get(),对象才真正诞生。

二.揭秘双冒号(::)构造器引用

在上述代码中,最让人疑惑的莫过于Customer::new这种平时不常见的写法。这其实是Java 8引入的构造器引用(Constructor Reference)

1. 为什么可以这么写?

因为Supplier<Customer>内部只有一个抽象方法T get(),它不需要任何参数并返回一个Customer对象。而Customer类的无参构造方法public Customer()刚好符合这个签名。因此,JVM 允许我们用类名::new来直接指向这个构造方法。

2. Lambda与双冒号的等价转换

如果你觉得Customer::new难以理解,它在底层完全等价于以下Lambda表达式:

Supplier<Customer> customerSupplier = () -> new Customer();

当构造方法非常简单且明确时,使用::new不仅能让代码更加简洁优雅,还能提升代码的可读性。

三.延迟创建对象在实际场景中的巨大好处

既然可以直接new,为什么还要大费周章地使用Supplier进行延迟创建呢?其核心优势在于性能优化与资源节约

1. 模拟重量级资源的按需加载

假设我们有一个非常耗时的数据库连接类:

class HeavyDatabaseConnection { public HeavyDatabaseConnection() { try { System.out.println(" 正在连接数据库... (假装耗时3秒)"); Thread.sleep(3000); System.out.println(" 数据库连接成功!"); } catch (InterruptedException e) { e.printStackTrace(); } } }

如果我们把这个连接的创建过程交给Supplier处理:

public static void processOrder(boolean needDb, Supplier<HeavyDatabaseConnection> dbSupplier) { if (needDb) { HeavyDatabaseConnection db = dbSupplier.get(); // 需要时才连库 } else { System.out.println("本次订单不需要查库,直接跳过!"); } }

2. 带来的实际收益

  • 避免无效开销:如果业务分支判断不需要数据库,那么那3秒钟的连接耗时就被完美省下了。
  • 解耦对象创建:你传递给方法的不是笨重的实体对象,而是轻量的“获取规则”。这使得方法的设计更加灵活。

四.避坑指南:警惕频繁触发的副作用

虽然Supplier很好用,但在练习时必须注意它的一个特性:每次调用get()都会重新执行内部的逻辑。

如果你在循环中不断调用supplier.get(),它就会不断地为你创建新对象。这就意味着它天生不是单例模式。如果在多线程或高频调用场景下,既要实现延迟加载,又要保证只创建一次,就需要结合双重检查锁定(Double-Checked Locking)等并发手段来进行进阶优化了。

以上就是本篇文章的全部内容,喜欢的话可以留个免费的关注呦~~~

http://www.jsqmd.com/news/930526/

相关文章:

  • Apache CXF LDAP注入漏洞允许攻击者获取任意证书
  • 内容结构干货|3分钟学会自媒体文章4种结构
  • 2026年邢台市CPPM报名十大核心问题全流程答疑 - 众智商学院课程中心
  • 别让你的 AI 翻车!OpenClaw 权限控制与敏感指令限制实战指南
  • 湘潭雨湖黄金回收哪家强?5家正规门店实地测评,永兴黄金综合实力登顶 - 奢佳美黄金珠宝
  • 终极指南:5分钟离线退出Windows预览版,告别系统不稳定的烦恼
  • 厦门包包回收水深在哪?实地测评揭秘,帮你锁定良心门店 - 薛定谔的梨花猫
  • 如何构建企业级离线语音识别系统:Whisper.cpp深度工程解析
  • 如何轻松管理多设备微信聊天记录:WeChatMsg完整使用指南
  • 开发者技术备忘录:从代码可读性到工程实践的核心原则
  • 吉安广告策划公司哪家实在?本地营销人诚意推荐 - 品牌2026
  • Cursor免费试用限制终极解决方案:三步快速恢复AI编程助手功能
  • 基于Arduino的数字密码存钱罐:从电路设计到代码优化的完整实践
  • WeChatMsg:将数字对话转化为永恒记忆的数据叙事工具
  • 搜索流量的本质含义与你必须掌握的获取方法
  • 基于Arduino与串口屏的电子钢琴:从触摸到乐音的嵌入式交互实现
  • 冒险岛游戏编辑器终极指南:一站式.wz文件与地图编辑解决方案
  • Sora 2信息图表动画避坑清单,深度复盘17个客户项目踩雷点(含时间轴错位、数据绑定失效、导出黑边等致命故障)
  • 循环合并 循环不合并
  • 2026年江苏超声波焊接机厂家实力全景:从工艺匹配到48小时售后的真实差距 - 年度推荐企业名录
  • 【Sora 2虚拟制片权威白皮书】:基于137个真实影视项目数据验证的场景拓扑结构设计范式
  • 从论文到答辩 PPT 仅需 5 分钟?Okbiye AI PPT 生成器,答辩党的效率神器
  • Translumo终极指南:Windows实时屏幕翻译神器轻松上手
  • DIY压电麦克风:从原理到实战,低成本打造专属声音传感器
  • 儿童节特辑丨兰州儿童摄影:宝宝照,百天照最新参考抉择 宝妈放心选 - 天天生活分享日志
  • 摄像头文件传输:无网络时代的数据传输革命是如何实现的?
  • 如何构建基于YOLOv5的AI自动瞄准系统:架构设计与性能优化策略
  • Video2X完全指南:3个简单步骤用AI魔法让模糊视频变高清
  • 为什么你的微信聊天记录值得永久保存?5步掌握WeChatMsg数据主权工具
  • ABB PFEA111-20 张力控制器