目录一.Supplier接口的核心概念与语法基础1. 什么是Supplier2. 基础热身感受延迟创建的威力二.揭秘双冒号::构造器引用1. 为什么可以这么写2. Lambda与双冒号的等价转换三.延迟创建对象在实际场景中的巨大好处1. 模拟重量级资源的按需加载2. 带来的实际收益四.避坑指南警惕频繁触发的副作用在Java 8引入Lambda表达式和函数式接口后我们的代码编写方式发生了巨大的改变。在日常开发中我们可能会遇到诸如SupplierCustomer customerSupplier Customer::new;这样看似“奇特”的写法。这其实涉及到了Java中的延迟初始化Lazy Initialization思想。本文将带你通过实战代码彻底搞懂这一套机制。一.Supplier接口的核心概念与语法基础1. 什么是Supplier在java.util.function包下SupplierT是一个标准的函数式接口。它的核心特征是无参数输入有返回值输出。你可以把它理解为一个“工厂”或者一个“待命的动作”它封装了创建某个对象的逻辑但不会立即执行。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 写法 ---); SupplierCustomer 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. 为什么可以这么写因为SupplierCustomer内部只有一个抽象方法T get()它不需要任何参数并返回一个Customer对象。而Customer类的无参构造方法public Customer()刚好符合这个签名。因此JVM 允许我们用类名::new来直接指向这个构造方法。2. Lambda与双冒号的等价转换如果你觉得Customer::new难以理解它在底层完全等价于以下Lambda表达式SupplierCustomer 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, SupplierHeavyDatabaseConnection dbSupplier) { if (needDb) { HeavyDatabaseConnection db dbSupplier.get(); // 需要时才连库 } else { System.out.println(本次订单不需要查库直接跳过); } }2. 带来的实际收益避免无效开销如果业务分支判断不需要数据库那么那3秒钟的连接耗时就被完美省下了。解耦对象创建你传递给方法的不是笨重的实体对象而是轻量的“获取规则”。这使得方法的设计更加灵活。四.避坑指南警惕频繁触发的副作用虽然Supplier很好用但在练习时必须注意它的一个特性每次调用get()都会重新执行内部的逻辑。如果你在循环中不断调用supplier.get()它就会不断地为你创建新对象。这就意味着它天生不是单例模式。如果在多线程或高频调用场景下既要实现延迟加载又要保证只创建一次就需要结合双重检查锁定Double-Checked Locking等并发手段来进行进阶优化了。以上就是本篇文章的全部内容喜欢的话可以留个免费的关注呦~~~
深入理解Java函数式编程:Supplier与延迟创建对象实战
发布时间:2026/6/1 17:15:18
目录一.Supplier接口的核心概念与语法基础1. 什么是Supplier2. 基础热身感受延迟创建的威力二.揭秘双冒号::构造器引用1. 为什么可以这么写2. Lambda与双冒号的等价转换三.延迟创建对象在实际场景中的巨大好处1. 模拟重量级资源的按需加载2. 带来的实际收益四.避坑指南警惕频繁触发的副作用在Java 8引入Lambda表达式和函数式接口后我们的代码编写方式发生了巨大的改变。在日常开发中我们可能会遇到诸如SupplierCustomer customerSupplier Customer::new;这样看似“奇特”的写法。这其实涉及到了Java中的延迟初始化Lazy Initialization思想。本文将带你通过实战代码彻底搞懂这一套机制。一.Supplier接口的核心概念与语法基础1. 什么是Supplier在java.util.function包下SupplierT是一个标准的函数式接口。它的核心特征是无参数输入有返回值输出。你可以把它理解为一个“工厂”或者一个“待命的动作”它封装了创建某个对象的逻辑但不会立即执行。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 写法 ---); SupplierCustomer 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. 为什么可以这么写因为SupplierCustomer内部只有一个抽象方法T get()它不需要任何参数并返回一个Customer对象。而Customer类的无参构造方法public Customer()刚好符合这个签名。因此JVM 允许我们用类名::new来直接指向这个构造方法。2. Lambda与双冒号的等价转换如果你觉得Customer::new难以理解它在底层完全等价于以下Lambda表达式SupplierCustomer 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, SupplierHeavyDatabaseConnection dbSupplier) { if (needDb) { HeavyDatabaseConnection db dbSupplier.get(); // 需要时才连库 } else { System.out.println(本次订单不需要查库直接跳过); } }2. 带来的实际收益避免无效开销如果业务分支判断不需要数据库那么那3秒钟的连接耗时就被完美省下了。解耦对象创建你传递给方法的不是笨重的实体对象而是轻量的“获取规则”。这使得方法的设计更加灵活。四.避坑指南警惕频繁触发的副作用虽然Supplier很好用但在练习时必须注意它的一个特性每次调用get()都会重新执行内部的逻辑。如果你在循环中不断调用supplier.get()它就会不断地为你创建新对象。这就意味着它天生不是单例模式。如果在多线程或高频调用场景下既要实现延迟加载又要保证只创建一次就需要结合双重检查锁定Double-Checked Locking等并发手段来进行进阶优化了。以上就是本篇文章的全部内容喜欢的话可以留个免费的关注呦~~~