Java优雅解决空指针问题源码级别刨析Optional 1

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Java优雅解决空指针问题源码级别刨析Optional

1 Optional介绍

在日常开发中,NullPointerException相信所有人都见过,不管你是刚入行的萌新还是骨灰级玩家,对于它都是耳熟能详的。它的出现可以说无处不在,总是能在各种场景下出现。那么对于如何防止它的出现,我们平时都是被动的采用各种非空校验,但是它还是经常能出现在我们的视线中。

public String getCompanyName(Student
student){
  if (student != null){
    Job job = student.getJob();
    if (job != null){
      Company company =
job.getCompany();
      if (company != null){
        String name =
company.getName();
        return name;
     }else {
        return "no company";
     }
   }else {
      return "no job";
   }
 }else {
    return "no student";
 }
}

对于上述这段代码,相信大家平时工作类似的代码经常会有出现。每一次在获取到一个对象时都进行一个null的判断,然后才继续后续的实现。但是这种方式很不好,首先会存在大量的if-else判断嵌套,导致代码的可读性和扩展性极差。此时,有的同学可能就会这么改造,如下所示:

public String getCompanyName(Student
student){
  if (student == null){
    return "no student";
 }
  Job job = student.getJob();
  if (job == null){
    return "no job";
 }
  Company company = job.getCompany();
  if (company == null){
    return "no company";
 }
  return company.getName();
}

这种判断已经有意识的避免了大量嵌套判断,但是同样存在多个不同的判断点,代码维护同样困难。

那么有没有一种方式可以优雅的解决这些问题呢?

2 Optional应用

为了防止空指针异常的出现,Java8中引入了一个新类Optional,对于它之前我们已经进行了简单的实现。其本质就是通过Optional类对值进行封装, 当有值的时候,会把该值封装到Optional类中。如果没有值的话,则会在该类封装一个Empty

在Optional类中基于函数式接口提供了一些用于操作值的方法。

2.1 创建Optional对象

要创建Optional,该类提供了三种方法操作,分别为:

empty()、of()、ofNullable()。使用方式如下所示:

Optional<Student> studentOptional =Optional.empty();
Optional<Student> studentOptional =Optional.of(student);
Optional<Student> studentOptional =Optional.ofNullable(student);

可以看到这三个方法,都会返回Optional对象,那么三者之间有什么区别呢?根据源码分析如下:

根据源码可知,empty()会直接返回一个空的Optional实例,内部不会存在任何值。

根据源码可知,of()会返回一个存在值的Optional对象,并且该值不允许null的存在。如果调用该方法时传入参数是null,则立刻抛出NullPointerException,而不是等到你用这个对象时才抛出,相当于进行了立即检查。

根据源码可知,ofNullable()同样也会返回一个存在值的Optional对象,但是它和of()最大的不同在于,它会对传入的值进行判断,如果传入的值为null,其会调用empty()返回一个不包含内容的Optional,如果不为null,则会调用of()返回一个包含内容的Optional

2.2 基于Optional对象获取值

有了Optional对象之后,就需求获取其内部的值了,Optional类也提供了多种方法用于值的获取。

2.2.1 isPresent()与ifPresent()应用&源码解析

Optional类中提供了两个方法用于判断Optional是否有值,分别是isPresent()和ifPresent(Consumer<? super T>consumer)。其一般与ofNullable()搭配使用,因为of()在创建时已经完成了判断,而empty()只是单纯了实例化了一个Optional对象。

根据源码可知,isPresent()内部非常简单,就是判断这个值是否为null。

根据源码可知,该方法在执行时,接收一个consumer函数式接口,如果value不为null,则通过consumer中的accept方法获取该值。

使用示例如下:

public class PresentDemo {
  public static void
getStudentName(Student student){
    Optional<Student> studentOptional =
Optional.ofNullable(student);
    if (studentOptional.isPresent()){
      //存在
      System.out.println("student存
在");
  }else {
      System.out.println("student不存
在");
   }
 }
  public static void main(String[] args) {
    Student student = new
Student(1,"zhangsan","M");
    getStudentName(student);
 }
}
Optional<Student> studentOptional =
Optional.ofNullable(student);
studentOptional.ifPresent(s->
System.out.println("学生存在"));

2.2.2 get()应用&源码解析

get()的使用非常简单,但不安全,因为其在获取值的时候,如果值存在,则直接返回封装在Optional中的值,如果不存在,则抛出NoSuchElementException。因此它的使用前提是已经确定Optional中有值,否则没有使用意义。

使用示例如下:

Optional<Student> studentOptional =
Optional.ofNullable(student);
if (studentOptional.isPresent()){
  Student result = studentOptional.get();
}

2.2.3 orElseThrow()应用&源码解析

该方法与get()类似,都是用于取值,但是当Optional中没有值时,get()会直接抛出

NoSuchElementException,这样的话,就存在了一定的局限性,因为有时可能需要抛出自定义异常。此时就可以使用orElseThrow(),它在取值时,如果Optional中没有值时,可以抛出自定义异常。

public class MyException extends Throwable {
  public MyException() {
    super();
 }
  public MyException(String message) {
    super(message);
 }
  @Override
  public String getMessage() {
    return "exception message";
 }
}
public class OrElseThrowDemo {
  public static void
getStudentInfo(Student student) {
    Optional<Student> studentOptional =
Optional.ofNullable(student);
   try {
      Student student1 =
studentOptional.orElseThrow(MyException::new
);
   } catch (MyException e) {
      e.printStackTrace();
   }
 }
  public static void main(String[] args) {
    Student student = null;
    getStudentInfo(student);
 }
}

2.2.4 map()应用&源码解析

当Option中有值的话,经常性的一个操作就是从值中获取它的某一个属性,实例如下:

if(job != null){
  String name = job.getName();
}

对于这种需求,可以通过map()完成,它的使用思路与Stream中的map类似,只不过一个是转换Stream的泛型,一个是转换Optional的泛型。

使用示例如下:

if (studentOptional.isPresent()){
  Optional<String> nameOptional =
studentOptional.map(Student::getName);
}


目录
相关文章
|
3天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
15 2
|
1月前
|
Java Apache Maven
Java百项管理之新闻管理系统 熟悉java语法——大学生作业 有源码!!!可运行!!!
文章提供了使用Apache POI库在Java中创建和读取Excel文件的详细代码示例,包括写入数据到Excel和从Excel读取数据的方法。
59 6
Java百项管理之新闻管理系统 熟悉java语法——大学生作业 有源码!!!可运行!!!
|
1月前
|
存储 C语言
【C语言篇】深入理解指针3(附转移表源码)
【C语言篇】深入理解指针3(附转移表源码)
36 1
|
2月前
|
数据采集 运维 前端开发
【Java】全套云HIS源码包含EMR、LIS (医院信息化建设)
系统技术特点:采用前后端分离架构,前端由Angular、JavaScript开发;后端使用Java语言开发。
79 5
|
7天前
|
人工智能 监控 数据可视化
Java智慧工地信息管理平台源码 智慧工地信息化解决方案SaaS源码 支持二次开发
智慧工地系统是依托物联网、互联网、AI、可视化建立的大数据管理平台,是一种全新的管理模式,能够实现劳务管理、安全施工、绿色施工的智能化和互联网化。围绕施工现场管理的人、机、料、法、环五大维度,以及施工过程管理的进度、质量、安全三大体系为基础应用,实现全面高效的工程管理需求,满足工地多角色、多视角的有效监管,实现工程建设管理的降本增效,为监管平台提供数据支撑。
24 3
|
12天前
|
运维 自然语言处理 供应链
Java云HIS医院管理系统源码 病案管理、医保业务、门诊、住院、电子病历编辑器
通过门诊的申请,或者直接住院登记,通过”护士工作站“分配患者,完成后,进入医生患者列表,医生对应开具”长期医嘱“和”临时医嘱“,并在电子病历中,记录病情。病人出院时,停止长期医嘱,开具出院医嘱。进入出院审核,审核医嘱与住院通过后,病人结清缴费,完成出院。
42 3
|
18天前
|
JavaScript Java 项目管理
Java毕设学习 基于SpringBoot + Vue 的医院管理系统 持续给大家寻找Java毕设学习项目(附源码)
基于SpringBoot + Vue的医院管理系统,涵盖医院、患者、挂号、药物、检查、病床、排班管理和数据分析等功能。开发工具为IDEA和HBuilder X,环境需配置jdk8、Node.js14、MySQL8。文末提供源码下载链接。
|
21天前
|
移动开发 前端开发 JavaScript
java家政系统成品源码的关键特点和技术应用
家政系统成品源码是已开发完成的家政服务管理软件,支持用户注册、登录、管理个人资料,家政人员信息管理,服务项目分类,订单与预约管理,支付集成,评价与反馈,地图定位等功能。适用于各种规模的家政服务公司,采用uniapp、SpringBoot、MySQL等技术栈,确保高效管理和优质用户体验。
|
1月前
|
JSON 前端开发 Java
震惊!图文并茂——Java后端如何响应不同格式的数据给前端(带源码)
文章介绍了Java后端如何使用Spring Boot框架响应不同格式的数据给前端,包括返回静态页面、数据、HTML代码片段、JSON对象、设置状态码和响应的Header。
132 1
震惊!图文并茂——Java后端如何响应不同格式的数据给前端(带源码)
|
1月前
|
存储 前端开发 Java
Java后端如何进行文件上传和下载 —— 本地版(文末配绝对能用的源码,超详细,超好用,一看就懂,博主在线解答) 文件如何预览和下载?(超简单教程)
本文详细介绍了在Java后端进行文件上传和下载的实现方法,包括文件上传保存到本地的完整流程、文件下载的代码实现,以及如何处理文件预览、下载大小限制和运行失败的问题,并提供了完整的代码示例。
438 1