深入理解Optional:处理空指针异常
1. 使用Optional处理可能为空的集合
在Java开发中,集合判空是一个常见但容易出错的场景。传统方式虽然可行,但存在一些潜在问题:
// 传统判空方式
if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {// print userInfo}
}
这种写法的缺点在于:
- 需要引入额外的工具类(CollectionUtils)
- 代码可读性较差,特别是当嵌套多层时
- 容易遗漏判空逻辑
使用Optional可以显著改善这种情况:
// 更合理的Optional使用方式
Optional.ofNullable(userInfoList).ifPresent(list -> {for (UserInfo userInfo : list) {// print userInfo}});
这种写法的优势:
- 明确表达了"如果存在则处理"的意图
- 链式调用更加流畅
- 减少了代码缩进层级
- 避免创建空集合对象
更进一步,我们可以结合Stream API:
Optional.ofNullable(userInfoList).stream().flatMap(Collection::stream).forEach(user -> {// 处理每个用户});
2. 深度嵌套对象的处理
对于深度嵌套的对象访问,传统判空方式会导致"金字塔式"代码:
// 传统方式
String city = null;
if (orderInfo != null) {Address address = orderInfo.getAddress();if (address != null) {city = address.getCity();}
}
这种代码不仅冗长,而且:
- 每增加一层嵌套,复杂度指数级增长
- 容易遗漏某些判空条件
- 可维护性差
使用Optional可以将其转化为流畅的链式调用:
// Optional方式
String city = Optional.ofNullable(orderInfo).map(Order::getAddress).map(Address::getCity).orElse("Unknown City");
或者当需要抛出异常时:
String city = Optional.ofNullable(orderInfo).map(Order::getAddress).map(Address::getCity).orElseThrow(() -> new IllegalStateException("OrderInfo or Address is null"));
2.1 实际应用场景扩展
考虑更复杂的业务场景,比如需要根据城市获取税率:
// 传统方式
Double taxRate = 0.0;
if (orderInfo != null) {Address address = orderInfo.getAddress();if (address != null) {String city = address.getCity();if (city != null) {TaxInfo taxInfo = taxService.getTaxInfo(city);if (taxInfo != null) {taxRate = taxInfo.getRate();}}}
}// Optional方式
Double taxRate = Optional.ofNullable(orderInfo).map(Order::getAddress).map(Address::getCity).map(taxService::getTaxInfo).map(TaxInfo::getRate).orElse(0.0);
3. Optional API深度解析
3.1 创建Optional对象
Optional提供了三种创建方式:
-
Optional.of(T value)
- 明确值不为null时使用Optional<String> opt = Optional.of("value"); // 如果传入null会抛出NPE
-
Optional.ofNullable(T value)
- 值可能为null时使用Optional<String> opt = Optional.ofNullable(maybeNullValue);
-
Optional.empty()
- 创建空OptionalOptional<String> opt = Optional.empty();
3.2 值获取与默认值处理
Optional提供了多种处理缺失值的方式:
-
orElse(T other)
- 提供默认值String value = optional.orElse("default");
-
orElseGet(Supplier<? extends T> other)
- 延迟计算默认值String value = optional.orElseGet(() -> expensiveOperation());
-
orElseThrow(Supplier<? extends X> exceptionSupplier)
- 抛出指定异常String value = optional.orElseThrow(() -> new CustomException("Not found"));
性能考虑:orElse()
的参数总是会被计算,而orElseGet()
只在需要时计算。对于代价高的操作,应使用orElseGet()
。
3.3 值转换与扁平化
-
map(Function<? super T, ? extends U> mapper)
- 值转换Optional<String> upper = optional.map(String::toUpperCase);
-
flatMap(Function<? super T, Optional<U>> mapper)
- 避免嵌套OptionalOptional<String> result = optional.flatMap(this::getOptionalValue);
区别示例:
Optional<Optional<String>> nested = optional.map(this::getOptionalValue); // Optional<Optional<String>>
Optional<String> flat = optional.flatMap(this::getOptionalValue); // Optional<String>
3.4 存在性检查
-
isPresent()
- 检查值是否存在if (optional.isPresent()) {// do something }
-
ifPresent(Consumer<? super T> consumer)
- 存在时执行操作optional.ifPresent(value -> process(value));
-
ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)
(Java 9+) - 存在或不存在时分别执行操作optional.ifPresentOrElse(value -> process(value),() -> log.warn("Value not present") );
4. 最佳实践与注意事项
-
不要将Optional用作字段或方法参数
Optional设计初衷是作为返回类型,用于明确表示方法可能不返回值。 -
避免Optional.get()
直接调用get()会抛出NoSuchElementException,应该使用orElse()等安全方法。 -
集合返回空集合而非Optional
对于集合返回类型,返回空集合比返回Optional.empty()更合适。 -
谨慎使用Optional.of()
只有确定值不为null时才使用of(),否则使用ofNullable()。 -
性能考虑
Optional会创建额外对象,在性能敏感的场景要谨慎使用。 -
与Stream结合使用
Java 9+中Optional新增了stream()方法,可以更好地与Stream API集成:List<String> names = Optional.ofNullable(userList).stream().flatMap(List::stream).map(User::getName).collect(Collectors.toList());
5. 总结
Optional是Java 8引入的强大工具,它:
- 明确表达了"可能没有值"的语义
- 减少了显式的null检查
- 提供了函数式风格的操作方法
- 使代码更加简洁和可读
通过合理使用Optional,我们可以编写出更安全的代码,有效减少空指针异常的发生。但同时也要注意它的适用场景,避免滥用。
相关文章:
深入理解Optional:处理空指针异常
1. 使用Optional处理可能为空的集合 在Java开发中,集合判空是一个常见但容易出错的场景。传统方式虽然可行,但存在一些潜在问题: // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...
从面试角度回答Android中ContentProvider启动原理
Android中ContentProvider原理的面试角度解析,分为已启动和未启动两种场景: 一、ContentProvider已启动的情况 1. 核心流程 触发条件:当其他组件(如Activity、Service)通过ContentR…...
OD 算法题 B卷【正整数到Excel编号之间的转换】
文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的:a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...
适应性Java用于现代 API:REST、GraphQL 和事件驱动
在快速发展的软件开发领域,REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名,不断适应这些现代范式的需求。随着不断发展的生态系统,Java 在现代 API 方…...

Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案
在大数据时代,海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构,在处理大规模数据抓取任务时展现出强大的能力。然而,随着业务规模的不断扩大和数据抓取需求的日益复杂,传统…...

pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)
目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 (1)输入单引号 (2)万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...

论文阅读笔记——Muffin: Testing Deep Learning Libraries via Neural Architecture Fuzzing
Muffin 论文 现有方法 CRADLE 和 LEMON,依赖模型推理阶段输出进行差分测试,但在训练阶段是不可行的,因为训练阶段直到最后才有固定输出,中间过程是不断变化的。API 库覆盖低,因为各个 API 都是在各种具体场景下使用。…...

【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制
目录 节点的功能承载层(GATT/Adv)局限性: 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能,如 Configuration …...

关于easyexcel动态下拉选问题处理
前些日子突然碰到一个问题,说是客户的导入文件模版想支持部分导入内容的下拉选,于是我就找了easyexcel官网寻找解决方案,并没有找到合适的方案,没办法只能自己动手并分享出来,针对Java生成Excel下拉菜单时因选项过多导…...

Ubuntu Cursor升级成v1.0
0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开,快捷键也不好用,当看到 Cursor 升级后,还是蛮高兴的 1. 下载 Cursor 下载地址:https://www.cursor.com/cn/downloads 点击下载 Linux (x64) ,…...
掌握 HTTP 请求:理解 cURL GET 语法
cURL 是一个强大的命令行工具,用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中,cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...

wpf在image控件上快速显示内存图像
wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像(比如分辨率3000*3000的图像)的办法,尤其是想把内存中的裸数据(只有图像的数据,不包…...

android RelativeLayout布局
<?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:gravity&…...

windows系统MySQL安装文档
概览:本文讨论了MySQL的安装、使用过程中涉及的解压、配置、初始化、注册服务、启动、修改密码、登录、退出以及卸载等相关内容,为学习者提供全面的操作指导。关键要点包括: 解压 :下载完成后解压压缩包,得到MySQL 8.…...

【Linux】自动化构建-Make/Makefile
前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具:make/makfile 1.背景 在一个工程中源文件不计其数,其按类型、功能、模块分别放在若干个目录中,mak…...

如何应对敏捷转型中的团队阻力
应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中,明确沟通敏捷转型目的尤为关键,团队成员只有清晰理解转型背后的原因和利益,才能降低对变化的…...
安卓基础(Java 和 Gradle 版本)
1. 设置项目的 JDK 版本 方法1:通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分,设置 Gradle JDK 方法2:通过 Settings File → Settings... (或 CtrlAltS)…...

华为OD机试-最短木板长度-二分法(A卷,100分)
此题是一个最大化最小值的典型例题, 因为搜索范围是有界的,上界最大木板长度补充的全部木料长度,下界最小木板长度; 即left0,right10^6; 我们可以设置一个候选值x(mid),将木板的长度全部都补充到x,如果成功…...
【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error
在前端开发中,JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作(如 Promise、async/await 等),开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝(r…...
在 Spring Boot 项目里,MYSQL中json类型字段使用
前言: 因为程序特殊需求导致,需要mysql数据库存储json类型数据,因此记录一下使用流程 1.java实体中新增字段 private List<User> users 2.增加mybatis-plus注解 TableField(typeHandler FastjsonTypeHandler.class) private Lis…...
「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案
在移动互联网营销竞争白热化的当下,推客小程序系统凭借其裂变传播、精准营销等特性,成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径,助力开发者打造具有市场竞争力的营销工具。 一、系统核心功能架构&…...

破解路内监管盲区:免布线低位视频桩重塑停车管理新标准
城市路内停车管理常因行道树遮挡、高位设备盲区等问题,导致车牌识别率低、逃费率高,传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法,正成为破局关键。该设备安装于车位侧方0.5-0.7米高度,直接规避树枝遮…...

通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器
拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件: 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...
libfmt: 现代C++的格式化工具库介绍与酷炫功能
libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库,提供了高效、安全的文本格式化功能,是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全:…...

在 Spring Boot 中使用 JSP
jsp? 好多年没用了。重新整一下 还费了点时间,记录一下。 项目结构: pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...

Chrome 浏览器前端与客户端双向通信实战
Chrome 前端(即页面 JS / Web UI)与客户端(C 后端)的交互机制,是 Chromium 架构中非常核心的一环。下面我将按常见场景,从通道、流程、技术栈几个角度做一套完整的分析,特别适合你这种在分析和改…...
LangFlow技术架构分析
🔧 LangFlow 的可视化技术栈 前端节点编辑器 底层框架:基于 (一个现代化的 React 节点绘图库) 功能: 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...

elementUI点击浏览table所选行数据查看文档
项目场景: table按照要求特定的数据变成按钮可以点击 解决方案: <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...