Java-空链基础入门
经过调研和细致观察,我们发现空链对于初次接触或是对Stream和Optional不太熟悉的人来说,确实存在一定的上手难度,宛如开启了“地狱模式”。为了降低这一门槛,我们决定通过一系列由简入深的案例演示,来逐步引导大家掌握空链的用法。当然,由于空链功能丰富,这里无法涵盖全部内容,但足以帮助大家入门。入门之后,建议结合相应的接口API进行深入学习和实践。为了方便大家在使用时快速找到参考,我们会在源码包中为每个方法都配备一份demo,确保大家在实践中能够有据可依,快速上手。
💡命名解释
空链里的“is”、“non”和“of”,一开始是不是让你觉得有点像谜语,不太好懂?别急,咱们来简单解密一下!
“is”就像侦探在问:“你是不是那个NULL?” 就像在玩捉迷藏,侦探在找NULL,问“你藏好了吗?”
“non NULL”呢,就像一个人在说:“我不是NULL,别搞错了!” 就像在一群人中,有个人站出来说自己不是那个要找的人。
“of NULL”就像团队标志,说:“我是NULL团队的!” 就像穿队服的人,一看就知道是哪个团队的。
现在,把这些词放在一起,是不是觉得它们都有了自己的小故事?
“is NULL”就是侦探找到了NULL,说:“就是你!”
“non NULL”就是那个人在说:“不是我,你找错人了!”
“of NULL”就是那个人在说:“我是NULL团队的,我们团队很有特色!”
这样一来,是不是觉得这些命名都变得简单有趣了?
💡神奇的 of
在空链中of 是核心中的核心,万物都是有一个规律从生到死, 从开始到结束,有始有终, 在浩瀚无垠的数字宇宙中,存在着一条名为“空链”的神秘链条,它超越了时间与空间的界限,承载着万物生灭的奥秘。在这条链条上,“of”是至高无上的符号,它既是起点,也是归宿,象征着一切有始有终的宇宙法则。
当第一缕数据之光闪烁时,“of”作为最初的指令被激活。它不仅是空链的基石,更是万物诞生的催化剂。随着“of”的觉醒,空链上开始涌现出各式各样的任务,每个任务都代表着一个生命或事物的初始形态,它们带着“of”赋予的潜能,踏上了各自的旅程。
在空链的流转中,每个任务都遵循着“of”所设定的规律,经历着从生到死、从开始到结束的完整周期。它们或成长、或繁衍、或衰老,每一步都伴随着数据的交换与能量的转化。而“of”作为这一切的见证者,始终默默地守护着每一个生命的轨迹,确保它们能够按照既定的轨迹前行。
当任务达到生命的终点时,它们并不真正消逝,而是化作空链上的一抹数据尘埃,回归到“of”的怀抱中。在这里,它们经历了短暂的休憩与重组,然后再次被“of”唤醒,以全新的形态重新踏上空链的旅程。这就是“of”所赋予的轮回之力,它让万物在生与死之间循环往复,生生不息。
空链安装教程
Hello World
String var= "Hello World";
Null.of(var).ifPresent(System.out::println);
这句代码的意思: 当 var 不是空的时候将执行 System.out::println 打印出 Hello World , 好了这只是小试牛刀,我们继续往下学习。
类型切换
类型切换在链式中非常重要,是最为核心和常用的操作,当一个对象是 User 其内部有一个 Role 对象,然后想获取 Role 的名称,那么我们该怎么操作呢?
//往常我们会这样处理
User user=...
if( user != null && user.getRole != null && user.getRole().getName != null ){//xxxxxxxx
}//如果用空链那么看看是怎么解决的
Null.of(user).map(User::getRole).map(Role::getName).ifPresent((name)->{//xxxx
})//或者
if(Null.of(user).map(User::getRole).map(Role::getName).non()){//xxxx
}
看样子空链并没有节约多少代码,那么空链的优势是啥?
- 代码理解与编写效率:熟悉空链后,你会发现其在写代码的效率比传统的if语句更高且更准确。空链的设计使得代码逻辑更加清晰,便于理解和维护。
- 高度可拔插性:空链过程每个节点都支持Lambda方式直接替换,这意味着你可以轻松地用静态方法或类方法替换某个节点的逻辑,而无需担心影响整体逻辑结构。这种高度可拔插的特性极大地提高了系统的灵活性和可扩展性。
- 避免空指针遗漏:空链的设计有效避免了空指针遗漏的问题,使得系统在运行过程中更加稳定可靠,减少了因空指针异常而导致的崩溃或错误。
复杂场景的效果如下: 这块逻辑就是通过协议号取出来费率, 如果有就直接返回了, 没有就返回 success。

**注意: **
- 绿色字体是我 IDEA 的一个插件自动把内部注释给提取出来了
- 在使用
orElse方法时,请注意其内部对默认值有限制。切勿将默认值设置为""(空字符串)、null、new ArrayList()(空列表)等无效或可能引起问题的值。这些做法通常意味着代码存在逻辑问题。请仔细思考默认值的用途,并确保其合理且有效。如果无法确定合适的默认值,建议考虑使用ifPresent或其他相关方法来处理。这些限制并非不合理,而是基于前人经验,旨在避免你陷入常见的编程陷阱。请遵守这些规范,以确保代码的健壮性和可靠性
多字段处理
需求: 当 user 对象内 a,b 字段不是空, 并且 c 是空的时候,并且 a==“123”, 才满足条件继续执行。
//往常我们会这样处理
User user=...
if( user != null && user.getA() != null && user.getB() !=null && user.getC() == null && "123".equals(user.getA())){//xxxxxxxxxxx
}
// 如果涉及是字符串的场景那么复杂度*2 , 如果用空链那么看看是怎么解决的
if(Null.of(user).of(User::getA).of(User::getB).isNull(User::getC).map(User::getA).eq("123")){//xxxxxxxxxxx
}
//简化写法
if(Null.of(user).ofAny(User::getA,User::getB).isNull(User::getC).map(User::getA).eq("123")){//xxxxxxxxxxx
}
扩展案例: 支持定制化的个性逻辑处理

流处理
Java 中的 Stream API 想必大家都不陌生,也都在日常开发中频繁使用过。Stream API 在处理列表、集合等相关数据结构时,极大地简化了代码逻辑,减少了代码量,并降低了复杂度。通过提供声明式编程风格,它让开发者能够更加专注于业务逻辑,而无需过多关注底层实现的细节。
Stream API 提供了丰富的操作方法,如过滤(filter)、映射(map)、排序(sorted)、聚合(collect)等,使得数据处理变得更加直观和高效。此外,Stream 还支持并行处理,能够充分利用多核 CPU 的优势,进一步提升处理性能。
在实际应用中,Stream API 已经成为处理集合数据的首选方式之一。它不仅能够提高开发效率,还能够提升代码的可读性和可维护性。
在空链中完美的集成了Stream, 让你从一个 空链 -》 流处理 -》 空链 在这个过程中的使用体验与Stream API无差异,你无需改变已有的编程习惯或思维方式。无论是过滤、映射、排序还是聚合等操作,都可以轻松地在空链中实现。这种集成不仅简化了数据处理逻辑,还提高了代码的可读性和可维护性。你可以更加专注于业务逻辑的实现,而无需过多关注底层数据结构的转换和处理。
因为大家都对流比较了解了, 这里直接贴上稍微复杂点的场景提供参考和思路。

如果上面的代码比较难理解可以像我这样展开写

万能时间处理
在空链中,我们精心提供了三种强大的时间处理功能:时间偏移、时间比较和时间格式化。这三种功能不仅基本覆盖了我们在日常工作中所需处理时间的大部分场景,而且其兼容性之广、处理范围之全面,使得它们成为了时间处理的“万能工具”。更令人欣喜的是,这些功能使用起来极其简单,无需复杂的操作或深厚的编程功底,即可轻松上手,满足各种时间处理需求。无论是进行时间的加减运算、比较两个时间点的先后,还是将时间格式化成特定的字符串形式,空链都能提供便捷、高效的解决方案。
需求: 获取当前月初的时间 , 获取当前月初+1 年-1 天的时间 , 数据存储要求为数字格式yyyyMMdd , 如果用空链实现效果如下:
//获取到月初的时间
Integer monthStart = Null.of(new Date()).dateOffset(DateOffsetEnum.START, TimeEnum.MONTHS).dateFormat(DateFormatEnum.NUM_DATE_PATTERN).map(Integer::parseInt).get();
System.out.println(monthStart); //20250301//拿到月初的时间直接在此基础上+1年 -1天, 返回的是传入的格式 yyyyMMdd
Integer endStart = Null.of(monthStart).dateOffset(DateOffsetEnum.ADD, 1, TimeEnum.YEARS).dateOffset(DateOffsetEnum.SUB, 1, TimeEnum.DAYS).get();
System.out.println(endStart); //20260228
支持的时间格式:
-
Date
-
LocalDate
-
LocalDateTime
-
10位时间戳(数字或者字符串)
-
13位时间戳(数字或者字符串)
-
格式化的时间格式字符串,
DateFormatEnum这里面有支持的全部类型
HTTP 化繁为简
在空链的 http 中所有的请求参数必须通过空链上个任务传递的, 不支持直接传递 , 兼容 Map 和 对象 请按照日常请求的规范来使用。
GET http://127.0.0.1:8798/user/userInfo/get?password=123456&username=admin
Map<String,String> from=new HashMap<>();
from.put("username","admin");
from.put("password","123456");
Null.of(from).http("http://127.0.0.1:8798/user/userInfo/get").get().toStr().ifPresent(System.out::println);UserInfo userEntity = new UserInfo();
userEntity.setUsername("huanmin");
userEntity.setPassword("123456");
Null.of(userEntity).http("http://127.0.0.1:8798/user/userInfo/get").get().toStr().ifPresent(System.out::println);String param = "username=admin&password=123456";
Null.of(param).http("http://127.0.0.1:8798/user/userInfo/get").get().toStr().ifPresent(System.out::println);String param1 = "username=admin";
Null.of(param1).http("http://127.0.0.1:8798/user/userInfo/get?password=123456").get().toStr().ifPresent(System.out::println);//参为Void.TYPE 表示没有参数
Null.of(Void.TYPE).http("http://www.baidu.com").get().toStr().ifPresent(System.out::println);
POST http://127.0.0.1:8798/user/userInfo/post/json
Map<String,Object> from=new HashMap<>();
from.put("username","admin");
from.put("password","123456");
Null.of(from).http("http://127.0.0.1:8798/user/userInfo/post/json").post(OkHttpPostEnum.JSON).toStr().ifPresent(System.out::println);UserInfo userEntity = new UserInfo();
userEntity.setUsername("huanmin");
userEntity.setPassword("123456");
Null.of(userEntity).http("http://127.0.0.1:8798/user/userInfo/post/json").post(OkHttpPostEnum.JSON).toStr().ifPresent(System.out::println);
注意: 发送的是 application/json; charset=utf-8协议, 数据会放入请求体中。
POST http://127.0.0.1:8798/user/login/post/form
Map<String,Object> from=new HashMap<>();
from.put("username","admin");
from.put("password","123456");
Null.of(from).http("http://127.0.0.1:8798/user/login/post/form").post(OkHttpPostEnum.FORM).toStr().ifPresent(System.out::println);
Null.of(userEntity).http("http://127.0.0.1:8798/user/login/post/form").post(OkHttpPostEnum.FORM).toStr().ifPresent(System.out::println);
注意: 发送的是application/x-www-form-urlencoded协议, 数据会放入请求体中。
POST http://127.0.0.1:8798/upload/file 文件上传
Map<String,Object> from=new HashMap<>();
from.put("file", new File(filePatch));
Null.of(from).http("http://127.0.0.1:8798/upload/file").post(OkHttpPostEnum.FILE).toStr().ifPresent(System.out::println);UserInfo userEntity = new UserInfo();
userEntity.setFile(new File(filePatch));
Null.of(userEntity).http("http://127.0.0.1:8798/upload/file").post(OkHttpPostEnum.FILE).toStr().ifPresent(System.out::println);
注意:
- 发送的是
multipart/form-data协议。 - 自动识别
File 和 byte[],File[] 和 byte[][] - 如果使用的是对象支持@JSONField 给属性起别名
POST http://127.0.0.1:8798/download/file 文件下载
String fileParam="filePath=test.nf";
Null.of(fileParam).http("http://127.0.0.1:8798/download/file").get().downloadFile("D:/test.nf").ifPresent(System.out::println);
Null.of(fileParam).http("http://127.0.0.1:8798/download/file").get().toBytes().ifPresent(System.out::println);
Null.of(fileParam).http("http://127.0.0.1:8798/download/file").get().toInputStream().ifPresent(System.out::println);
还有其他的各种操作这里就不演示了, 支持 GET POST DELETE PUT 这常用的 4 种协议, 以及一些常规的操作。
扩展
静态方法
目前空链中的静态方法, 后期可能会不断地增加, 静态方法在lambda 中是可以直接通过 类名::方法名 称表示的 ,这样可以在很多场景下非常容易直接嵌入到链式代码中。

上图可能不全可以自行到Null 和 NullUtil里面查看
空链核心的 4 种接口
空链核心提供了四种接口,涵盖了其所有关键功能:
- NullChain:这是空链的过程方法接口,汇聚了所有处理流程中的核心方法。
- NullConvert:此接口专注于转换方法,提供了各种将数据类型进行转换的实用工具。
- NullTools:作为工具方法接口,它汇集了空链中所有辅助性和实用性的方法,便于日常开发使用。
- NullFinality:这是空链的终结方法接口,包含了所有用于结束处理流程或确定最终结果的方法。
通过这四种接口,您可以轻松找到空链的所有相关方法调用,以及深入探索其各种内部实现API。
更多高级特性
Java-空链手册
相关文章:
Java-空链基础入门
经过调研和细致观察,我们发现空链对于初次接触或是对Stream和Optional不太熟悉的人来说,确实存在一定的上手难度,宛如开启了“地狱模式”。为了降低这一门槛,我们决定通过一系列由简入深的案例演示,来逐步引导大家掌握…...
【江协科技STM32】Unix时间戳BKP备份寄存器RTC实时时钟(学习笔记)
Unix时间戳 Unix 时间戳(Unix Timestamp)定义为从UTC/GMT的1970年1月1日0时0分0秒开始所经过的秒数,不考虑闰秒时间戳存储在一个秒计数器中,秒计数器为32位/64位的整型变量世界上所有时区的秒计数器相同,不同时区通过…...
PCDN网络设备
PCDN(Peer-to-Peer Content Delivery Network,点对点内容分发网络)是一种基于P2P技术的内容分发网络。它利用用户终端设备之间的直接数据传输来减少中心服务器的压力,并提高内容交付的速度和效率。 PCDN的工作原理 节点共享&…...
3.17-3.23 Web3 游戏周报:Pixudi 双榜领跑,The Forgotten Runiverse 登陆三大主机平台
回顾上周的区块链游戏概况,查看 Footprint Analytics 与 ABGA 最新发布的数据报告。 【3.17–3.23】Web3 游戏行业动态 Ronin 将与 Alpha Growth 等合作推出 1300 万美元增长计划,以向 DeFi 扩张Notcoin 开发工作室 Open Builders 宣布推出 Not Games …...
AppInventor2生成3位数的水仙花数
生成3位水仙花数(每位数字的立方之和刚好等于这个数字)的代码,如下: 来源:【生成Python】AppInventor2中文网已支持代码块转换Python源码! - App Inventor 2 中文网 - 清泛IT社区,为创新赋能&…...
【聚类算法解析系列02】经典聚类算法(上)——K-Means与层次聚类
【聚类算法解析系列02】经典聚类算法(上)——K-Means与层次聚类 引言:算法背后的认知革命 K-Means与层次聚类,这两个诞生于1960年代的算法,至今仍是工业界使用率最高的聚类工具。它们分别代表了两种根本性的世界观&am…...
shadcn如何给dialog增加关闭按钮和隐藏右上角关闭按钮
增加关闭按钮: <DialogFooter><DialogClose asChild><button className"rounded-sm bg-black/100 px-3 py-2 text-xs font-semibold text-white shadow-xs hover:bg-black/90" >Close</button></DialogClose> </DialogF…...
华为机试牛客刷题之HJ59 找出字符串中第一个只出现一次的字符
HJ59 找出字符串中第一个只出现一次的字符 描述 对于给定的字符串,找出第一个只出现一次的字符。如果不存在,则输出 −1。 输入描述: 在一行上输入一个长度为 1≦len(s)≦10^3 、仅由小写字母构成的字符串 s。 输出描述: 如果存…...
C# 中实现一个线程持续读取,另一个线程负责写入,且写入时读取线程暂停
实现思路 暂停信号:通过 ManualResetEventSlim 通知读取线程暂停。 暂停确认:读取线程收到暂停信号后,发送确认信号。 原子性控制:确保写入操作执行期间,读取线程处于完全暂停状态。 恢复机制:写入完成后…...
[Effective C++]条款22:将成员变量声明为private
. 在C中,将成员变量声明为private而不是public,主要是为了遵循面向对象编程(OOP)的封装原则。他有助于隐藏对象的内部实现细节,提供更好地控制,安全性和可维护性。 1、数据隐藏与封装 将成员变量声明为pr…...
心法利器[132] | 大模型系统性能优化trick
心法利器 本栏目主要和大家一起讨论近期自己学习的心得和体会。具体介绍:仓颉专项:飞机大炮我都会,利器心法我还有。 2023年新的文章合集已经发布,获取方式看这里:又添十万字-CS的陋室2023年文章合集来袭,更…...
Android第六次面试总结(Java设计模式篇一)
单例模式属于创建型设计模式,它保证一个类仅有一个实例,并且提供一个全局访问点来获取该实例。下面为你详细阐述单例模式的好处和坏处。 好处 资源优化:单例模式能保证一个类只有一个实例,这对于那些创建和销毁开销大的对象&…...
stm第九天433M无线遥控灯
1.433M无线模块工作原理 数据发射模块的工作频率为315M,采用声表谐振器SAW稳频,频率稳定度极高,当环境温度在-25~85度之间变化时,频飘仅为3ppm.接收到信号,接收模块对应针脚输出高电平,有DO D1 D2 D3&#…...
六十天前端强化训练之第三十天之深入解析Vue3电商项目:TechStore全栈实践(文结尾附有源代码)
欢迎来到编程星辰海的博客讲解 看完可以给一个免费的三连吗,谢谢大佬! 目录 深入解析Vue3电商项目:TechStore全栈实践 一、项目架构设计 二、核心功能实现 三、组合式API深度实践 四、性能优化实践 五、项目扩展方向 六、开发经验总结…...
类与对象(中)(详解)
【本节目标】 1. 类的6个默认成员函数 2. 构造函数 3. 析构函数 4. 拷贝构造函数 5. 赋值运算符重载 6. const成员函数 7. 取地址及const取地址操作符重载 1.类的6个默认成员函数 如果一个类中什么成员都没有,简称为空类。 空类中真的什么都没有吗&…...
Spring Boot框架中常用注解
以下是Spring Boot框架中常用注解的详细说明,包括名称、用途、用法、使用位置及扩展示例,按功能模块分类整理: 一、核心启动与配置注解 1. SpringBootApplication 用途:主启动类注解,整合了 Configuration、EnableAu…...
ResNet与注意力机制:深度学习中的强强联合
引言 在深度学习领域,卷积神经网络(CNN)一直是图像处理任务的主流架构。然而,随着网络深度的增加,梯度消失和梯度爆炸问题逐渐显现,限制了网络的性能。为了解决这一问题,ResNet(残差…...
notify_one() 会阻塞吗?
notify_one() 不会阻塞。它是用于唤醒一个等待中的线程,通常是通过条件变量(std::condition_variable)来使用的。调用 notify_one() 会使一个处于等待状态的线程被唤醒并继续执行,但它本身并不会阻塞。 当调用 notify_one() 时&a…...
Flutter项目之页面实现以及路由fluro
目录: 1、项目代码结构2、页面编写以及路由配置main.dart(入口文件)page_content.dartindex.dartapplication.dartpubspec.yamllogin.dartdio_http.dart 3、Fluro路由routes.dartnot_found_page.dart(路由优化,找不到页面时展示此页面) 4、注册页面 1、项…...
《Python实战进阶》第31集:特征工程:特征选择与降维技术
第31集:特征工程:特征选择与降维技术 摘要 特征工程是机器学习和数据科学中不可或缺的一环,其核心目标是通过选择重要特征和降低维度来提升模型性能并减少计算复杂度。本集聚焦于特征选择与降维技术,涵盖过滤法、包裹法、嵌入法等…...
大模型在支气管哮喘手术全流程风险预测与治疗方案制定中的应用研究
目录 一、引言 1.1 研究背景与意义 1.2 研究目标与方法 1.3 研究创新点 二、支气管哮喘概述 2.1 定义与发病机制 2.2 分类与临床表现 2.3 诊断标准与方法 三、大模型技术原理与应用现状 3.1 大模型的基本原理 3.2 在医疗领域的应用案例分析 3.3 适用于支气管哮喘预…...
C++类与对象的第二个简单的实战练习-3.24笔记
哔哩哔哩C面向对象高级语言程序设计教程(118集全) 实战二 Cube.h #pragma once class Cube { private:double length;double width;double height; public:double area(void);double Volume(void);//bool judgement(double L1, double W1, double H1);…...
react自定义hook
自定义hook: 用来封装复用的逻辑,,自定义hook是以use开头的普通函数,,将组件中可复用的状态逻辑抽取到自定义的hook中,简化组件代码 常见自定义hook例子: 封装一个简单的计数器 import {useS…...
Rk3568驱动开发_设备树点亮LED_10
设备树中添加节点 在设备树文件中添加led节点,添加完后需要重新编译内核,因为单独编译这个设备树文件生成的dtb文件目前不能直接做替换,所以要编译内核将编译好的boot.img文件烧录到设备里,boot.img里包含新添加的设备树节点&…...
大数据学习(82)-数仓详解
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言📝支持一…...
Unity学习之Shader(Phong与Blinn-Phong)
三、Lesson3 1、关键名称 向量 • nDir:法线方向,点乘操作时简称n; • lDir:光照方向,点乘操作时简称l; • vDir:观察方向,点乘操作时简称v; • rDir:光反…...
uniapp笔记-swiper组件实现轮播图
思路 主要就是参考 swiper | uni-app官网 实现轮播图。 实例 新建一个banner.vue通用组件。 代码如下: <template><view>轮播图</view> </template><script> </script><style> </style> 随后在index.vue中导…...
【C++ 继承】—— 青花分水、和而不同,继承中的“明明德”与“止于至善”
欢迎来到ZyyOvO的博客✨,一个关于探索技术的角落,记录学习的点滴📖,分享实用的技巧🛠️,偶尔还有一些奇思妙想💡 本文由ZyyOvO原创✍️,感谢支持❤️!请尊重原创…...
FPGA_YOLO(二)
上述对cnn卷积神经网络进行介绍,接下来对YOLO进行总结,并研究下怎么在FPGA怎么实现的方案。 对于一个7*7*30的输出 拥有49个cell 每一个cell都有两个bbox两个框,并且两个框所包含的信息拥有30个 4个坐标信息和一个置信度5个,剩下就是20个类别。 FPGA关于YOLO的部署 1…...
蓝桥杯学习-14子集枚举,二进制枚举
子集枚举 一、回溯3-子集枚举(递归实现指数型枚举) 一旦涉及选与不选,删和不删,留和不留-->两种状态-->就要想到子集枚举例题1–递归实现指数型枚举19685 其实看不懂这个题目,好奇怪的题目。根据老师的解析来写…...
