高效处理NPE!!

news2024/9/20 1:02:24

相信不少小伙伴已经被java的NPE(Null Pointer Exception)所谓的空指针异常搞的头昏脑涨,有大佬说过“防止 NPE,是程序员的基本修养。”但是修养归修养,也是我们程序员最头疼的问题之一,那么我们今天就要尽可能的利用Java8的新特性 Optional来尽量简化代码同时高效处理NPE(Null Pointer Exception 空指针异常)

认识Optional并使用

简单来说,Opitonal类就是Java提供的为了解决大家平时判断对象是否为空用 会用 null!=obj 这样的方式存在的判断,从而令人头疼导致NPE(Null Pointer Exception 空指针异常),同时Optional的存在可以让代码更加简单,可读性跟高,代码写起来更高效.

常规判断:

//对象 人
//属性有 name,age
Person person=new Person();
if (null==person){
   return person为null;
}
return person;

使用Optional:

//对象 人
//属性有 name,age
Person person=new Person();
return Optional.ofNullable(person).orElse(person为null);

测试展示类Person代码(如果有朋友不明白可以看一下这个):

public class Person {
    private String name;
    private Integer age;

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public Person() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

下面,我们就高效的学习一下神奇的Optional类!

2.1 Optional对象创建

首先我们先打开Optional的内部,去一探究竟 先把几个创建Optional对象的方法提取出来

public final class Optional<T> {
   private static final Optional<?> EMPTY = new Optional<>();
   private final T value;
   //我们可以看到两个构造方格都是private 私有的
   //说明 我们没办法在外面去new出来Optional对象
   private Optional() {
        this.value = null;
    }
   private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }
    //这个静态方法大致 是创建出一个包装值为空的一个对象因为没有任何参数赋值
   public static<T> Optional<T> empty() {
        @SuppressWarnings(unchecked)
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }
    //这个静态方法大致 是创建出一个包装值非空的一个对象 因为做了赋值
   public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }
    //这个静态方法大致是 如果参数value为空,则创建空对象,如果不为空,则创建有参对象
   public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }
 }

再做一个简单的实例展示 与上面对应

// 1、创建一个包装对象值为空的Optional对象
Optional<String> optEmpty = Optional.empty();
// 2、创建包装对象值非空的Optional对象
Optional<String> optOf = Optional.of(optional);
// 3、创建包装对象值允许为空也可以不为空的Optional对象
Optional<String> optOfNullable1 = Optional.ofNullable(null);
Optional<String> optOfNullable2 = Optional.ofNullable(optional);

我们关于创建Optional对象的内部方法大致分析完毕 接下来也正式的进入Optional的学习与使用中。

2.2 Optional.get()方法(返回对象的值)

get()方法是返回一个option的实例值 源码:

public T get() {
    if (value == null) {
        throw new NoSuchElementException(No value present);
    }
    return value;
}

也就是如果value不为空则做返回,如果为空则抛出异常 No value present 简单实例展示

Person person=new Person();
person.setAge(2);
Optional.ofNullable(person).get();

2.3 Optional.isPresent()方法(判读是否为空)

isPresent()方法就是会返回一个boolean类型值,如果对象不为空则为真,如果为空则false 源码:

public Boolean isPresent() {
    return value != null;
}

简单的实例展示:

Person person=new Person();
person.setAge(2);
if (Optional.ofNullable(person).isPresent()){
    //写不为空的逻辑
    System.out.println(不为空);
} else{
    //写为空的逻辑
    System.out.println(为空);
}

2.4 Optional.ifPresent()方法(判读是否为空并返回函数)

这个意思是如果对象非空,则运行函数体 源码:

  public void ifPresent(Consumer<? super T> consumer) {
        //如果value不为空,则运行accept方法体
        if (value != null)
            consumer.accept(value);
    }

看实例:

Person person=new Person();
person.setAge(2);
Optional.ofNullable(person).ifPresent(p -> System.out.println(年龄+p.getAge()));

如果对象不为空,则会打印这个年龄,因为内部已经做了NPE(非空判断),所以就不用担心空指针异常了。

2.5 Optional.filter()方法(过滤对象)

filter()方法大致意思是,接受一个对象,然后对他进行条件过滤,如果条件符合则返回Optional对象本身,如果不符合则返回空Optional

源码:

public Optional<T> filter(Predicate<? super T> predicate) {
    Objects.requireNonNull(predicate);
    //如果为空直接返回this
    if (!isPresent())
                return this; else
            //判断返回本身还是空Optional
    return predicate.test(value) ? this : empty();
}

简单实例:

Person person=new Person();
person.setAge(2);
Optional.ofNullable(person).filter(p -> p.getAge()>50);

2.6 Optional.map()方法(对象进行二次包装)

map()方法将对应Funcation函数式接口中的对象,进行二次运算,封装成新的对象然后返回在Optional中 源码:

 public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        //如果为空返回自己
        if (!isPresent())
            return empty();
        else {
        //否则返回用方法修饰过的Optional
            return Optional.ofNullable(mapper.apply(value));
        }
    }

实例展示:

Person person1=new Person();
person.setAge(2);
String optName = Optional.ofNullable(person).map(p -> person.getName()).orElse(name为空);

2.7 Optional.flatMap()方法(Optional对象进行二次包装)

map()方法将对应Optional< Funcation >函数式接口中的对象,进行二次运算,封装成新的对象然后返回在Optional中 源码:

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

实例:

Person person=new Person();
person.setAge(2);
Optional<Object> optName = Optional.ofNullable(person).map(p -> Optional.ofNullable(p.getName()).orElse(name为空));

2.8 Optional.orElse()方法(为空返回对象)

常用方法之一,这个方法意思是如果包装对象为空的话,就执行orElse方法里的value,如果非空,则返回写入对象 源码:

public T orElse(T other) {
    //如果非空,返回value,如果为空,返回other
    return value != null ? value : other;
}

2.9 Optional.orElseGet()方法(为空返回Supplier对象)

这个与orElse很相似,入参不一样,入参为Supplier对象,为空返回传入对象的.get()方法,如果非空则返回当前对象 源码:

public T orElseGet(Supplier<? extends T> other) {
    return value != null ? value : other.get();
}

实例:

Optional<Supplier<Person>> sup=Optional.ofNullable(Person::new);
//调用get()方法,此时才会调用对象的构造方法,即获得到真正对象
Optional.ofNullable(person).orElseGet(sup.get());

说真的对于Supplier对象我也懵逼了一下,去网上简单查阅才得知 Supplier也是创建对象的一种方式,简单来说,Suppiler是一个接口,是类似Spring的懒加载,声明之后并不会占用内存,只有执行了get()方法之后,才会调用构造方法创建出对象创建对象的语法的话就是Supplier supPerson= Person::new;需要使用时supPerson.get()即可

2.10 Optional.orElseThrow()方法(为空返回异常)

这个我个人在实战中也经常用到这个方法,方法作用的话就是如果为空,就抛出你定义的异常,如果不为空返回当前对象,在实战中所有异常肯定是要处理好的,为了代码的可读性

源码:

    public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (value != null) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }

实例:这个就贴实战源码了

//简单的一个查询
Member member = memberService.selectByPhone(request.getPhone());
Optional.ofNullable(member).orElseThrow(() -> new ServiceException(没有查询的相关数据));

2.11 相似方法进行对比分析

可能小伙伴看到这,没用用过的话会觉得orElse()和orElseGet()还有orElseThrow()很相似,map()和flatMap()好相似

哈哈哈不用着急,都是从这一步过来的,我再给大家总结一下不同方法的异同点

orElse()和orElseGet()和orElseThrow()的异同点

方法效果类似,如果对象不为空,则返回对象,如果为空,则返回方法体中的对应参数,所以可以看出这三个方法体中参数是不一样的

  • orElse(T 对象)

  • orElseGet(Supplier < T >对象)

  • orElseThrow(异常)

map()和orElseGet的异同点

  • 方法效果类似,对方法参数进行二次包装,并返回,入参不同

  • map(function函数)

  • flatmap(Optional< function >函数)

具体要怎么用,要根据业务场景以及代码规范来定义,下面可以简单看一下我在实战中怎用使用神奇的Optional

3.实战场景再现

场景1:

在service层中查询一个对象,返回之后判断是否为空并做处理

//查询一个对象
Member member = memberService.selectByIdNo(request.getCertificateNo());
//使用ofNullable加orElseThrow做判断和操作
Optional.ofNullable(member).orElseThrow(() -> new ServiceException(没有查询的相关数据));

场景2:

我们可以在dao接口层中定义返回值时就加上Optional 例如:我使用的是jpa,其他也同理

public interface LocationRepository extends JpaRepository<Location, String> {
Optional<Location> findLocationById(String id);
}

然在是Service中

public TerminalVO findById(String id) {
//这个方法在dao层也是用了Optional包装了
        Optional<Terminal> terminalOptional = terminalRepository.findById(id);
        //直接使用isPresent()判断是否为空
        if (terminalOptional.isPresent()) {
        //使用get()方法获取对象值
            Terminal terminal = terminalOptional.get();
            //在实战中,我们已经免去了用set去赋值的繁琐,直接用BeanCopy去赋值
            TerminalVO terminalVO = BeanCopyUtils.copyBean(terminal, TerminalVO.class);
            //调用dao层方法返回包装后的对象
            Optional<Location> location = locationRepository.findLocationById(terminal.getLocationId());
            if (location.isPresent()) {
                terminalVO.setFullName(location.get().getFullName());
            }
            return terminalVO;
        }
        //不要忘记抛出异常
        throw new ServiceException(该终端不存在);
    }

4.Optional使用注意事项

Optional真么好用,真的可以完全替代if判断吗?

我想这肯定是大家使用完之后Optional之后可能会产生的想法,答案是否定的

举一个最简单的栗子:

例子1:

如果我只想判断对象的某一个变量是否为空并且做出判断呢?

Person person=new Person();
person.setName();
persion.setAge(2);
//普通判断
if(StringUtils.isNotBlank(person.getName())){
  //名称不为空执行代码块
}
//使用Optional做判断
Optional.ofNullable(person).map(p -> p.getName()).orElse(name为空);

我觉得这个例子就能很好的说明这个问题,只是一个很简单判断,如果用了Optional我们还需要考虑包装值,考虑代码书写,考虑方法调用,虽然只有一行,但是可读性并不好,如果别的程序员去读,我觉得肯定没有if看的明显

5.jdk1.9对Optional优化

首先增加了三个方法:

or()、ifPresentOrElse() 和 stream()

or() 与orElse等方法相似,如果对象不为空返回对象,如果为空则返回or()方法中预设的值。

ifPresentOrElse() 方法有两个参数:一个 Consumer 和一个 Runnable。如果对象不为空,会执行 Consumer 的动作,否则运行 Runnable。相比ifPresent()多了OrElse判断。

stream() 将Optional转换成stream,如果有值就返回包含值的stream,如果没值,就返回空的stream。

因为这个jdk1.9的Optional具体我没有测试,同时也发现有蛮好的文章已经也能让大家明白jdk1.9的option的优化,我就不深入去说了。

 ——EOF——


福利:

扫码回复【酒店】可免费领取酒店管理系统源码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2147463.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

视觉语言大模型模型介绍-CLIP学习

多模态学习领域通过结合图像和文本信息&#xff0c;为各种视觉语言任务提供了强大的支持。图像和文本的结合在人工智能领域具有重要的意义&#xff0c;它使得机器能够更全面地理解人类的交流方式。通过这种结合&#xff0c;模型能够处理包括图像描述、视觉问答、特征提取和图像…

Maya动画基础

Maya动画基础教程&#xff08;完整&#xff09;_哔哩哔哩_bilibili 第一集 动画基础设置 altv播放动画 选择撕下副本 右键---播放预览 第二集 k帧记录物体的空间信息 初始位置清零 删除历史记录 s键key帧 自动记录位置信息 删除帧&#xff0c;按住右键选择delete 按shif…

Apache subversion 编译流程

目录 1. 概述2. 依赖库简介2.1 Expat2.2 Apache apr2.3 Apache apr-iconv2.4 Apache apr-util2.5 Zlib2.6 OpenSSL2.7 Sqlite2.8 Apache Serf2.9 Apache subversion3. 编译3.1 Expat编译3.1.1 源码信息3.1.2 CMake-GUI3.1.3 编译步骤3.2 APR编译3.2.1 源码信息3.2.2 编译步骤3.…

详解c++菱形继承和多态---下

菱形继承 #include<iostream>using namespace std; class Animal { public:int m_Age; }; class Sheep : public Animal {}; class Tuo : public Animal {}; class SheepTuo : public Sheep, public Tuo {}; void test() {SheepTuo st;st.Sheep::m_Age 18;st.Tuo::m_Age…

数据结构-3.1.栈的基本概念

一.栈的定义&#xff1a; 栈和线性表的区别&#xff1a;栈只能在表尾一端进行插入或者删除的操作&#xff0c;而线性表可以在任意一个地方进行插入或者删除 二.有关栈的关键术语&#xff1a; 三.栈的基本操作&#xff1a; 1.回顾线性表的基本操作&#xff1a; 2.栈的基本操作&…

vue使用vue-i18n实现国际化

我使用的是vue2.6版本&#xff0c;具体使用其他版本可以进行修改 一、安装 npm install vue-i18n -D 二、配置 1、文件配置 ①在src下创建 i18n 目录 ②在 i18n 目录下创建 langs 文件夹 和 index.js文件&#xff0c;具体如下 2、index.js代码如下&#xff0c;这里使用了…

【分立元件】案例:新人加了个TVS管为什么可能导致系统不能正常工作

因为最近在带多个新人,让其设计原理图和PCB总会发现各种电路问题点。比如TVS管接法问题。 TVS是一种限压型的过压保护器,它将过高的电压钳制至一个安全范围,藉以保护后面的电路,有着比其它保护元件更快的反应时间,这使TVS可用在防护lighting、switching、ESD等快速破坏性瞬…

计算机毕业设计 教师科研信息管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

【字符函数】strcpy函数(字符串复制函数)+strcat函数(字符串追加)+strcmp函数(字符串比较)【笔记】

1.复制函数--------------strcpy函数 函数使用 char*strcpy&#xff08;char* destination, const char* source&#xff09; strcpy函数用于拷贝字符串&#xff0c;即将一个字符串中的内容拷贝到另一个字符串中&#xff08;会覆盖原字符串内容&#xff09;。它的参数是两个指…

TIOBE 编程指数 9 月排行榜公布 VB.Net第七

原文地址&#xff1a;百度安全验证 IT之家 9 月 8 日消息&#xff0c;TIOBE 编程社区指数是一个衡量编程语言受欢迎程度的指标&#xff0c;评判的依据来自世界范围内的工程师、课程、供应商及搜索引擎&#xff0c;今天 TIOBE 官网公布了 2024 年 9 月的编程语言排行榜&#xf…

Element走马灯组件循环播放两个页面是方向不一致

摘要&#xff1a;使用Carousel 走马灯循环播放同一类型的图片、文字等内容&#xff0c;会在循环内容为两组是出现下图 [1]中的现象。本文记录下如何解决 之前项目遇到过一次这个问题&#xff0c;由于indicator-position 指示器不用显示&#xff0c;则判断内容长度为2时&#xf…

恶意Bot流量识别分析实践

1、摘要 随着互联网的发展&#xff0c;自动化工具和脚本&#xff08;Bots&#xff09;的使用越来越普遍。虽然一些善意 Bots 对于网站的正常运行和数据采集至关重要&#xff0c;但恶意 Bots 可能会对网站带来负面影响&#xff0c;如爬取敏感信息、恶意注册、刷流量等。因此&am…

saltstack配置管理

一、saltstack的SSH工作模式 一、salt-ssh介绍 salt-ssh 是 0.17.0 新引入的一个功能&#xff0c;不需要minion对客户端进行管理&#xff0c;也不需要master。salt-ssh 支持salt大部分的功能&#xff1a;如 grains、modules、state 等salt-ssh 没有使用ZeroMQ的通信架构&#…

向日葵好用吗?4款稳定的远程控制软件推荐。

远程控制技术现在已经被应用于很多个领域&#xff0c;像企业办公&#xff0c;远程协助&#xff0c;智能家居&#xff0c;工业控制等等。我们常常会用到的时前两种。而实现远程控制的方式也有多种&#xff0c;但是最方便高效的还是使用第三方软件。我最常使用的是向日葵&#xf…

算法.图论-并查集上

文章目录 1. 并查集介绍2. 并查集的实现2.1 实现逻辑2.2 isSameSet方法2.3 union方法(小挂大优化)2.4 find方法(路径压缩优化) 3. 并查集模板 1. 并查集介绍 定义&#xff1a; 并查集是一种树型的数据结构&#xff0c;用于处理一些不相交集合的合并及查询问题&#xff08;即所…

游戏如何对抗定制挂

近年来&#xff0c;游戏安全对抗强度相比以往更加激烈&#xff0c;具体表现在“定制挂”趋势显著。在近期收集的近万款外挂样本中&#xff0c;定制挂约占比78%&#xff0c;常见的内存修改器、变速器等通用作弊手段占比正在下降。 所谓定制挂&#xff0c;是指针对某款游戏单独开…

SPI接口通信协议浅谈成都自动化开发

沙鸥-成都 1 什么是SPI SPI是串口外设接口的缩写&#xff0c;是一种高速的、全双工、同步的通信协议&#xff0c;是微处理器与外围IC之间常用的一种通讯方式。 SPI是主从式的通信协议&#xff0c;可以一主机一从机通信&#xff0c;也可以一主机多从机通信。 2 SPI的优缺点 SPI接…

【Java版】云HIS系统源码

云HIS系统介绍 云HIS系统是一款满足基层医疗机构各类业务需要的健康云产品。该产品能帮助基层医疗机构完成日常各类业务&#xff0c;提供病患挂号支持、病患问诊、电子病历、开药发药、会员管理、统计查询、医生站和护士站等一系列常规功能&#xff0c;还能与公卫、PACS等各类…

【STM32 HAL库】OLED显示模块

【STM32 HAL库】OLED显示模块 前言理论OLED基本参数OLED基本驱动原理OLED坐标轴 应用CubeMx配置底层函数代码高层封装函数printf显示函数 前言 本文为笔者学习 OLED 的总结&#xff0c;基于keysking的视频内容&#xff0c;如有错误&#xff0c;欢迎指正 理论 OLED基本参数 …

基于224G的超高速以太网端口1.6Tbps 1600G真的来了~

基于224G PAM4 SerDes的1.6T(更激进些的是3.2T&#xff09; 受AI智能算中心的驱动&#xff0c;基于4x112G的400G光模块&#xff0c;和基于8x112G的800G的光模块已经很成熟了&#xff0c;标志就是大家都在降本增效&#xff0c;考虑干掉功耗的DSP&#xff0c;...另外一个标志就是…