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月前
|
XML Java 编译器
Java注解的底层源码剖析与技术认识
Java注解(Annotation)是Java 5引入的一种新特性,它提供了一种在代码中添加元数据(Metadata)的方式。注解本身并不是代码的一部分,它们不会直接影响代码的执行,但可以在编译、类加载和运行时被读取和处理。注解为开发者提供了一种以非侵入性的方式为代码提供额外信息的手段,这些信息可以用于生成文档、编译时检查、运行时处理等。
100 7
|
13天前
|
前端开发 JavaScript Java
[Java计算机毕设]基于ssm的OA办公管理系统的设计与实现,附源码+数据库+论文+开题,包安装调试
OA办公管理系统是一款基于Java和SSM框架开发的B/S架构应用,适用于Windows系统。项目包含管理员、项目管理人员和普通用户三种角色,分别负责系统管理、请假审批、图书借阅等日常办公事务。系统使用Vue、HTML、JavaScript、CSS和LayUI构建前端,后端采用SSM框架,数据库为MySQL,共24张表。提供完整演示视频和详细文档截图,支持远程安装调试,确保顺利运行。
55 17
|
20天前
|
Java 调度
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
当我们创建一个`ThreadPoolExecutor`的时候,你是否会好奇🤔,它到底发生了什么?比如:我传的拒绝策略、线程工厂是啥时候被使用的? 核心线程数是个啥?最大线程数和它又有什么关系?线程池,它是怎么调度,我们传入的线程?...不要着急,小手手点上关注、点赞、收藏。主播马上从源码的角度带你们探索神秘线程池的世界...
91 0
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
|
1月前
|
JavaScript 安全 Java
智慧产科一体化管理平台源码,基于Java,Vue,ElementUI技术开发,二开快捷
智慧产科一体化管理平台覆盖从备孕到产后42天的全流程管理,构建科室协同、医患沟通及智能设备互联平台。通过移动端扫码建卡、自助报道、智能采集数据等手段优化就诊流程,提升孕妇就诊体验,并实现高危孕产妇五色管理和孕妇学校三位一体化管理,全面提升妇幼健康宣教质量。
52 12
|
8天前
|
JavaScript Java Docker
干货含源码!如何用Java后端操作Docker(命令行篇)
只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
1月前
|
人工智能 监控 安全
Java智慧工地(源码):数字化管理提升施工安全与质量
随着科技的发展,智慧工地已成为建筑行业转型升级的重要手段。依托智能感知设备和云物互联技术,智慧工地为工程管理带来了革命性的变革,实现了项目管理的简单化、远程化和智能化。
43 5
|
15天前
|
存储 监控 数据可视化
SaaS云计算技术的智慧工地源码,基于Java+Spring Cloud框架开发
智慧工地源码基于微服务+Java+Spring Cloud +UniApp +MySql架构,利用传感器、监控摄像头、AI、大数据等技术,实现施工现场的实时监测、数据分析与智能决策。平台涵盖人员、车辆、视频监控、施工质量、设备、环境和能耗管理七大维度,提供可视化管理、智能化报警、移动智能办公及分布计算存储等功能,全面提升工地的安全性、效率和质量。
|
2月前
|
JavaScript Java 测试技术
基于Java+SpringBoot+Vue实现的车辆充电桩系统设计与实现(系统源码+文档+部署讲解等)
面向大学生毕业选题、开题、任务书、程序设计开发、论文辅导提供一站式服务。主要服务:程序设计开发、代码修改、成品部署、支持定制、论文辅导,助力毕设!
|
2月前
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
121 7
|
3月前
|
存储 JavaScript 前端开发
基于 SpringBoot 和 Vue 开发校园点餐订餐外卖跑腿Java源码
一个非常实用的校园外卖系统,基于 SpringBoot 和 Vue 的开发。这一系统源于黑马的外卖案例项目 经过站长的进一步改进和优化,提供了更丰富的功能和更高的可用性。 这个项目的架构设计非常有趣。虽然它采用了SpringBoot和Vue的组合,但并不是一个完全分离的项目。 前端视图通过JS的方式引入了Vue和Element UI,既能利用Vue的快速开发优势,
183 13