百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

Java中NullPointerException的完美解决方案

lipiwang 2025-03-06 16:38 26 浏览 0 评论

null在Java中带来的麻烦

我相信所有的Java程序猿一定都遇到过NullPointerException,空指针在Java程序中是最常见的,也是最烦人的;它让我们很多程序猿产生了根深蒂固的感觉,所有可能产生空指针的地方都得加上if-else检查,但是这带给我们很多麻烦

  • Java本身是强类型的,但是null破坏了这个规则,它可以被赋值给任何对象
  • Java的设计是让程序猿对指针无感知,但是null指针是个例外
  • 它会是代码变得很臃肿,到处都充斥着if-else的空检查,甚至是多层嵌套,代码可读性下降
  • null本身毫无意义,表示不了

前两点不需要特别的说明,后两点举个例子来说明一下:假如一个人拥有一个手机,每个手机都有生成厂商,每个厂商都会有个名字,用类表示的话:

public class Person {
    private Phone phone;

    public Phone getPhone() {
        return phone;
    }
}

public class Phone {
    private Producer producer;

    public Producer getProducer() {
        return producer;
    }
}

public class Producer {
    private String name;

    public String getName() {
        return name;
    }
}

在这个例子中,假如我们需要取到手机生成厂商的名字

public String getPhoneProducerName(Person person) {
    return person.getPhone().getProducer().getName();
}

由于不一定每个人都会有一个手机,所有在调用getProducer()时可能会出现NullPointerException

一门设计语言本来就是来描述世界的,在这个事例中有的人有手机,有的人也可能没有手机,所以在调用person.getPhone()返回的值就应该包含有和无这两种情况,现在通过返回null来表示无,但是在调用getProducer()却又会抛出异常,这样就不太符合现实逻辑;所以把null来用来表示不合适

在遇到这种情况通常的做法是做null检查,甚至是每个地方可能发生null指针的做检查。

public String getPhoneProducerName(Person person) {
    if (person.getPhone() == null) {
        return "无名字";
    }
    if (person.getPhone().getProducer() == null) {
        return "无名字";
    }
    return person.getPhone().getProducer().getName();
}

这里我已经试图在减少代码的层级,如果使用的是if-else,代码的层级会更深,代码可读性下降。


Optional的简单介绍

吐槽了那么多现状的不好,现在可以祭出我们的解决方案了 Optional;千呼万唤始出来,犹抱琵琶半遮面;那Optional到底是个什么东西,我们一起来逐步解开它的面纱。

image

Optional本身只是对对象的简单包装,如果对象为空,那么会构建一个空的Optional;这样一来Optional就包含了存在和不存在两个情况, 接下来可以看下上面的例子改过之后

public class Person {
    private Optional phone;

    public Optional getPhone() {
        return phone;
    }
}

public class Phone {
    private Producer producer;

    public Producer getProducer() {
        return producer;
    }
}

public class Producer {
    private String name;

    public String getName() {
        return name;
    }
}

由于有的人可能没有手机,有的人有,所以Phone需要用Optional包装起来;手机本身一定会有生产的厂商,厂商一定会有一个名字,所以这两个不需要用Optional包装起来。这里我们会发现使用了Optional会丰富代码的语义,让代码更加符合现实。

而当我们在调用phone.getProducer().getName()的时候不需要做null指针的检查,如果说在这里发生了NullPointerException,说明这里数据本身是有问题的,不符合现实,就应该让问题暴露出来,而不是像上面的代码一样把问题掩盖。


Optional的常用方法使用

1. Optional的创建方法

Optional empty = Optional.empty();  //申明一个空的Optional
Optional person = Optional.of(new Person()); //包装Person
Optional person2 = Optional.of(null); //不允许的操作,传入null 会抛出空指针异常
Optional optionalPerson = Optional.ofNullable(null); //允许传null, 返回一个空的Optional

2. Optional值的获取方式

  • map、flatMap 首先我们重新定义一下Phone类,除了有生产厂商之外,还有个型号;
public class Phone {
    private String model;
    private Producer producer;

    public Producer getProducer() {
        return producer;
    }
    public String getModel() {
        return model;
    }
}

当我们需要获取到手机的型号的时候可以这样:

Optional optionalPhone = Optional.of(new Phone());
Optional model = optionalPhone.map(Phone::getModel);

当我们需要通过Person对象获取到Phone的型号,会想到这样:

Optional optionalPerson = Optional.of(new Person());
optionalPerson.map(Person::getPhone).map(Phone::getModel);

当我们写出来的时候发现编译器不能通过。是因为Person::getPhone返回的是一个Optional,调用optionalPerson.map(Person::getPhone)返回的就是Optional<Optional>,所以再.map的就无法拿到手机型号,那如何能够让返回的结果不是Optional<Optional>,而是Optional呢?

这里需要用到另一个方法flatMapflatMapmap的区别,我在刚开始学习的时候,看到了网上的各种解释都很绕,看的很晕,最后直接打开源码来看,发现实现很简单,很容易理解,来看下源码:

public Optional map(Function mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent())
        return empty();
    else {
        return Optional.ofNullable(mapper.apply(value));
    }
}
public Optional flatMap(Function<? super T, Optional> mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent())
        return empty();
    else {
        return Objects.requireNonNull(mapper.apply(value));
    }
}

map方法在返回的时候会包装一层OptionalflatMap在返回的时候直接把函数的返回值返回了,函数的结果必须是Optional;那么在前面的例子中我们直接调用flatMap返回的结果就是Optional

Optional optionalPerson = Optional.of(new Person());
optionalPerson.flatMap(Person::getPhone).map(Phone::getModel);
  • 取出Optional中的值对象:get、orElse、orElseGet、orElseThrow、ifPresent
  1. get() : 当你明确知道Optional中有值的话可以直接调用该方法,当Optional中没有值是该方法会抛出异常NoSuchElementException;所以当如果存在空值的话建议就不要调用该方法,因为这样和做null检查就没有区别了
  2. orElse(T other) : 提供一个默认值,当值不存在是返回这个默认值
  3. orElseGet(Supplier other) : 当值不存在的时候会调用supper函数,如果说返回这个默认值的逻辑较多,那么调用这个方法比较合适;
  4. orElseThrow(Supplier exceptionSupplier) : 当值为空时会抛出一个自定义的异常
  5. ifPresent(Consumer consumer) : 当值不为空是会调用consumer函数,如果值为空,那么这个方法什么都不做
  • filter 过滤出满足条件的对象 假如我们需要过滤出手机型号IOS的手机,并打印出型号,代码如下:
Person person = new Person(Optional.of(new Phone("IOS")));
        Optional optionalPerson = Optional.of(person);
        optionalPerson.flatMap(Person::getPhone)
                .filter(phone -> "IOS".equals(phone.getModel()))
                .map(Phone::getModel)
                .ifPresent(System.out::println);

总结

  1. 我们讨论了null在Java程序的问题
  2. 介绍Java8中引入了Optional来表示有和无的情况以及初始化的方式
  3. 举例说明了Optional中经常使用到的方法





















原创 Silently9527 贝塔学JAVA

https://mp.weixin.qq.com/s/6XGH813YD2jvyNhaqqNi9g

相关推荐

ubuntu单机安装open-falcon极度详细操作

备注:以下操作均由本人实际操作并得到验证,喜欢的同学可尝试操作安装。步骤一1.1环境准备(使用系统:ubuntu18.04)1.1.1安装redisubuntu下安装(参考借鉴:https://...

Linux搭建promtail、loki、grafana轻量日志监控系统

一:简介日志监控告警系统,较为主流的是ELK(Elasticsearch、Logstash和Kibana核心套件构成),虽然优点是功能丰富,允许复杂的操作。但是,这些方案往往规模复杂,资源占用高,...

一文搞懂,WAF阻止恶意攻击的8种方法

WAF(Web应用程序防火墙)是应用程序和互联网流量之间的第一道防线,它监视和过滤Internet流量以阻止不良流量和恶意请求,WAF是确保Web服务的可用性和完整性的重要安全解决方案。它...

14配置appvolume(ios14.6配置文件)

使用AppVolumes应用程序功能,您可以管理应用程序的整个生命周期,包括打包、更新和停用应用程序。您还可以自定义应用程序分配,以向最终用户提供应用程序的特定版本14.1安装appvolume...

目前流行的缺陷管理工具(缺陷管理方式存在的优缺点)

摘自:https://blog.csdn.net/jasonteststudy/article/details/7090127?utm_medium=distribute.pc_relevant.no...

开源数字货币交易所开发学习笔记(2)——SpringCloud

前言码云(Gitee)上开源数字货币交易所源码CoinExchange的整体架构用了SpringCloud,对于经验丰富的Java程序员来说,可能很简单,但是对于我这种入门级程序员,还是有学习的必要的...

开发JAX-RPC Web Services for WebSphere(下)

在开发JAX-RPCWebServicesforWebSphere(上)一文中,小编为大家介绍了如何创建一个Web服务项目、如何创建一个服务类和Web服务,以及部署项目等内容。接下来小编将为大...

CXF学习笔记1(cxf client)

webservice是发布服务的简单并实用的一种技术了,个人学习了CXF这个框架,也比较简单,发布了一些笔记,希望对笔友收藏并有些作用哦1.什么是webServicewebService让一个程序可...

分布式RPC最全详解(图文全面总结)

分布式通信RPC是非常重要的分布式系统组件,大厂经常考察的Dubbo等RPC框架,下面我就全面来详解分布式通信RPC@mikechen本篇已收于mikechen原创超30万字《阿里架构师进阶专题合集》...

Oracle WebLogic远程命令执行0day漏洞(CVE-2019-2725补丁绕过)预警

概述近日,奇安信天眼与安服团队通过数据监控发现,野外出现OracleWebLogic远程命令执行漏洞最新利用代码,此攻击利用绕过了厂商今年4月底所发布的最新安全补丁(CVE-2019-2725)。由...

Spring IoC Container 原理解析(spring中ioc三种实现原理)

IoC、DI基础概念关于IoC和DI大家都不陌生,我们直接上martinfowler的原文,里面已经有DI的例子和spring的使用示例《InversionofControlContainer...

Arthas线上服务器问题排查(arthas部署)

1Arthas(阿尔萨斯)能为你做什么?这个类从哪个jar包加载的?为什么会报各种类相关的Exception?我改的代码为什么没有执行到?难道是我没commit?分支搞错了?遇到问题无法在...

工具篇之IDEA功能插件HTTP_CLENT(idea2021插件)

工具描述:Java开发人员通用的开发者工具IDEA集成了HTTPClient功能,之后可以无需单独安装使用PostMan用来模拟http请求。创建方式:1)简易模式Tools->HTTPCl...

RPC、Web Service等几种远程监控通信方式对比

几种远程监控通信方式的介绍一.RPCRPC使用C/S方式,采用http协议,发送请求到服务器,等待服务器返回结果。这个请求包括一个参数集和一个文本集,通常形成“classname.meth...

《github精选系列》——SpringBoot 全家桶

1简单总结1SpringBoot全家桶简介2项目简介3子项目列表4环境5运行6后续计划7问题反馈gitee地址:https://gitee.com/yidao620/springbo...

取消回复欢迎 发表评论: