告别空指针让代码变优雅,Optional使用图文例子源码解读

news2024/9/24 6:11:23

一、前言

我们在开发中最常见的异常就是NullPointerException,防不胜防啊,相信大家肯定被坑过!

这种基本出现在获取数据库信息中、三方接口,获取的对象为空,再去get出现!

解决方案当然简单,只需要判断一下,不是空在去后续操作,为空返回!

所有在JDK8时出现了专门处理的方案,出来很早了,但是小编惭愧一直没有去使用它!

最近在看《Java开发手册》,一直想着提高自己的代码水平,文中就指出了使用Optional来解决NullPointerException

二、Java开发手册规范

小编使用的是2022版的黄山版,29页写到:

【推荐】防止 NPE,是程序员的基本修养,注意 NPE 产生的场景:

  • 返回类型为基本数据类型,return 包装数据类型的对象时,自动拆箱有可能产生 NPE

反例:public int method() { return Integer 对象; },如果为 null,自动解箱抛 NPE。

  • 数据库的查询结果可能为 null。
  • 集合里的元素即使 isNotEmpty,取出的数据元素也可能为 null。
  • 远程调用返回对象时,一律要求进行空指针判断,防止 NPE。
  • 对于 Session 中获取的数据,建议进行 NPE 检查,避免空指针。
  • 级联调用 obj.getA().getB().getC();一连串调用,易产生 NPE。

正例:使用 JDK8 的 Optional 类来防止 NPE 问题。

这份手册还是不错的,推荐反复阅读,虽然进不去大厂,也要自觉约束自己的代码风格,努力向大厂靠!

大家现在不知道哪里找的可以下载一下:

《Java开发手册》

三、Optional常用方法

小编带大家一起从api文档中的方法,一个个带大家慢慢去了解它!

1. empty()

返回一个空的Optional实例:Optional.empty

Optional<Object> empty = Optional.empty();
log.info("empty值:{}",empty);

在这里插入图片描述

2. of(T value)

传入一个参数,返回一个Optional对象,如果参数为空,报NullPointerException

Test testNew  = new Test();
Test test = null;
Optional<Test> optionalNew = Optional.of(testNew);
log.info(" optional对象:{}",optionalNew);
Optional<Test> optional = Optional.of(test);

在这里插入图片描述

源码查看:

我们看到参数为空会报NullPointerException,我们去方法内部看一下就明白了:

public static <T> Optional<T> of(T value) {
    return new Optional<>(value);
}
private Optional(T value) {
    this.value = Objects.requireNonNull(value);
}
public static <T> T requireNonNull(T obj) {
    if (obj == null)
        throw new NullPointerException();
    return obj;
}

我们发现是在Objects类中的requireNonNull方法中判断了是否为空!

这个还会出现NullPointerException,所以我们一般使用下面的这个方法!

3. ofNullable(T value)

参数传入一个对象,返回一个Optional对象,如果为空,将返回一个空的Optional对象,就等于Optional.empty

Test testNew  = new Test();
Test test = null;
Optional<Test> optionalNew = Optional.of(testNew);
log.info(" optional对象:{}",optionalNew);

Optional<Test> optionalTest = Optional.ofNullable(test);
log.info(" optional对象中的ofNullable方法返回值:{}",optionalTest);
Optional<Test> optionalTestNew = Optional.ofNullable(testNew);
log.info(" optional对象中的ofNullable方法new返回值:{}",optionalTestNew);

在这里插入图片描述

源码查看:

我们发现是在方法开始进行非空判断,再去调用上面的of(T value)方法

public static <T> Optional<T> ofNullable(T value) {
    return value == null ? empty() : of(value);
}

4. get()

如果此Optional中存在值,则返回该值,否则抛出NoSuchElementException

Test testNew  = new Test();
Test test = null;
Optional<Test> optionalNew = Optional.of(testNew);
log.info(" optional对象:{}",optionalNew);
// Optional<Test> optional = Optional.of(test);

Optional<Test> optionalTest = Optional.ofNullable(test);
log.info(" optional对象中的ofNullable方法返回值:{}",optionalTest);
Optional<Test> optionalTestNew = Optional.ofNullable(testNew);
log.info(" optional对象中的ofNullable方法new返回值:{}",optionalTestNew);

Test test2 = optionalTestNew.get();
log.info("原来有值的:经过Optional包装后get后得到原来的值:{}",test2);
Test test1 = optionalTest.get();
log.info("原来没有值的:经过Optional包装后get后得到原来的值:{}",test1);

在这里插入图片描述

源码查看:

调用开始会进行值判断,如果为空则抛异常!

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

5. isPresent()

如果存在值,则返回true,否则返回false。

这里代码就不加上面的,大家参考上面的获取一个Optional对象

boolean present = optionalTestNew.isPresent();
log.info("optionalTestNew调用是否为空:{}",present);
boolean present1 = optionalTest.isPresent();
log.info("optionalTest调用是否为空:{}",present1);

在这里插入图片描述

源码查看:

这就比较简单了!

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

6. ifPresent(Consumer<? super T> consumer)

如果存在值,则使用该值调用指定的使用者,否则不执行任何操作。

主要的就是入参数一个函数式接口,有值就会去执行,为空则不进行任何操作!

小技巧:

开始对lambda不了解时,可以先按照上面这种方式进行写,

大家可以看到Idea给置灰了,就是可以优化,我们Alt+Enter
然后再次Enter就会变成后面的lambda!

在这里插入图片描述

optionalTest.ifPresent(new Consumer<Test>() {
    @Override
    public void accept(Test test) {
        log.info("我是调用ifPresent执行后的打印=====");
    }
});
optionalTestNew.ifPresent(testInner -> log.info("我是调用ifPresent执行后的打印"));

在这里插入图片描述

源码查看:

还是先判断不为空才去执行函数式接口!

public void ifPresent(Consumer<? super T> consumer) {
    if (value != null)
        consumer.accept(value);
}

7. filter(Predicate<? super T> predicate)

如果存在值,并且该值符合规则,则返回描述该值的Optional,否则返回空Optional

是一个Predicate函数接口,可以传入实现了Predicate接口的lambda表达式!
如果不符合条件就会返回一个Optional.empty

testNew.setName("萧炎");
testNew.setAge(33);
Optional<Test> optionalTest1 = optionalTestNew.filter(test1 -> test1.getAge() > 30);
log.info("过滤后的结果:{}",optionalTest1.get());

在这里插入图片描述

源码查看:

就是判断一下表达式和值是否为空,然后就是根据规则判断

public Optional<T> filter(Predicate<? super T> predicate) {
   Objects.requireNonNull(predicate);
    if (!isPresent())
        return this;
    else
        return predicate.test(value) ? this : empty();
}

8. map(Function<? super T,? extends U> mapper)

如果存在值,则将提供的映射函数应用于该值,如果结果为非空,则返回描述结果的Optional。否则,返回空的Optional。

也是一个函数式接口!

Optional<String> stringOptional = optionalTestNew.map(Test::getName);
log.info("map后获得字段值:{}",stringOptional.get());

在这里插入图片描述

源码查看:

也是进行非空判断,然后执行lambda得到字段后放到ofNullable方法中!

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

9. flatMap(Function<? super T,Optional> mapper)

如果存在值,则将提供的Optional方位映射函数应用于该值,返回该结果,否则返回空的Optional。此方法类似于map,但提供的映射器的结果已经是可选的,并且如果调用,flatMap不会不会在最后进行任何包装。

Optional<String> optional = optionalTestNew.flatMap(OptionalTest::getFlatMap);
log.info("flatMap后得到的字段:{}",optional.get());

private static Optional<String> getFlatMap(Test test){
    return Optional.ofNullable(test).map(Test::getName);
}

在这里插入图片描述
源码查看:

也是进行非空判断,然后和map不同的是不执行ofNullable方法

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));
    }
}

10. orElse(T other)

如果有值则将其返回,否则返回指定的其它值。

如果你是一个对象,orElse()也要是相同对象!

String message = null;
String messageNew = "关注公众号:小王博客基地";

String nullString = Optional.ofNullable(message).orElse("这是一个空字符串!");
log.info("这是空字符串打印的:{}",nullString);
String string = Optional.ofNullable(messageNew).orElse("=====这是一个空字符串!");
log.info("这是字符串打印的:{}",string);

在这里插入图片描述

源码查看:

简单的为空返回自己定义的,不为空直接返回!

public T orElse(T other) {
    return value != null ? value : other;
}

11. orElseGet(Supplier<? extends T> other)

返回值(如果存在),否则调用other并返回该调用的结果。

区别:
orElse方法将传入的参数作为默认值,orElseGet方法可以接受Supplier接口的实现用来生成默认值

如果没有复杂操作,Idea也会提醒我们不要使用这个,使用orElse即可!

String message = null;
String messageNew = "关注公众号:小王博客基地";
String orElseGet = Optional.ofNullable(message).orElseGet(() -> "这还是一个空的字符串");
log.info("orElseGet调用:这是空字符串打印的:{}",orElseGet);
String orElseGetString = Optional.ofNullable(messageNew).orElseGet(() -> "这还是一个空的字符串");
log.info("orElseGet调用:这是字符串打印的:{}",orElseGetString);

在这里插入图片描述

源码查看:

和orElse一样,只不过为空调用lambda执行!

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

12. orElseThrow(Supplier<? extends X> exceptionSupplier)

返回包含的值(如果存在),否则抛出由提供的供应商创建的异常。

String message = null;
String messageNew = "关注公众号:小王博客基地";
Optional.ofNullable(messageNew).orElseThrow(() -> new RuntimeException("为空了,还不看看!"));
Optional.ofNullable(message).orElseThrow(() -> new RuntimeException("为空了,还不看看!"));

我们可以自定义异常,然后来引用!

在这里插入图片描述

源码查看:

为空则走自己写的异常!

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

13. 例子汇总

/**
 * @author wangzhenjun
 * @date 2023/2/27 10:22
 */
@Slf4j
public class OptionalTest {

    public static void main(String[] args) {

        Optional<Object> empty = Optional.empty();
        log.info("empty值:{}",empty);


        Test testNew  = new Test();
        Test test = null;
        Optional<Test> optionalNew = Optional.of(testNew);
        log.info(" optional对象:{}",optionalNew);
//        Optional<Test> optional = Optional.of(test);

        Optional<Test> optionalTest = Optional.ofNullable(test);
        log.info(" optional对象中的ofNullable方法返回值:{}",optionalTest);
        Optional<Test> optionalTestNew = Optional.ofNullable(testNew);
        log.info(" optional对象中的ofNullable方法new返回值:{}",optionalTestNew);

        Test test2 = optionalTestNew.get();
        log.info("原来有值的:经过Optional包装后get后得到原来的值:{}",test2);
        // Test test1 = optionalTest.get();
        // log.info("原来没有值的:经过Optional包装后get后得到原来的值:{}",test1);

        boolean present = optionalTestNew.isPresent();
        log.info("optionalTestNew调用是否为空:{}",present);
        boolean present1 = optionalTest.isPresent();
        log.info("optionalTest调用是否为空:{}",present1);

        optionalTest.ifPresent(new Consumer<Test>() {
            @Override
            public void accept(Test test) {
                log.info("我是调用ifPresent执行后的打印=====");
            }
        });
        optionalTestNew.ifPresent(testInner -> log.info("我是调用ifPresent执行后的打印"));

        testNew.setName("萧炎");
        testNew.setAge(33);
        Optional<Test> optionalTest1 = optionalTestNew.filter(test1 -> test1.getAge() > 30);
        log.info("过滤后的结果:{}",optionalTest1.get());

        Optional<String> stringOptional = optionalTestNew.map(Test::getName);
        log.info("map后获得字段值:{}",stringOptional.get());

        Optional<String> optional = optionalTestNew.flatMap(OptionalTest::getFlatMap);
        log.info("flatMap后得到的字段:{}",optional.get());

        String message = null;
        String messageNew = "关注公众号:小王博客基地";

        String nullString = Optional.ofNullable(message).orElse("这是一个空字符串!");
        log.info("这是空字符串打印的:{}",nullString);
        String string = Optional.ofNullable(messageNew).orElse("=====这是一个空字符串!");
        log.info("这是字符串打印的:{}",string);

        String orElseGet = Optional.ofNullable(message).orElseGet(() -> "这还是一个空的字符串");
        log.info("orElseGet调用:这是空字符串打印的:{}",orElseGet);
        String orElseGetString = Optional.ofNullable(messageNew).orElseGet(() -> "这还是一个空的字符串");
        log.info("orElseGet调用:这是字符串打印的:{}",orElseGetString);

        Optional.ofNullable(messageNew).orElseThrow(() -> new RuntimeException("为空了,还不看看!"));
        Optional.ofNullable(message).orElseThrow(() -> new RuntimeException("为空了,还不看看!"));



    }

    private static Optional<String> getFlatMap(Test test){
        return Optional.ofNullable(test).map(Test::getName);
    }

}

四、总结

这里就不在演示实战了,基本上组合使用:

Optional.ofNullable(需要判断的对象).ifPresent(具体操作)

其实和if相比就是显得优雅一些,主要是防止某处没考虑到,忘记if判断,那么后续可能会导致空指针,如果使用Optional的话,那么这个问题能够得到避免。

就像多使用设计模式一样,让自己的代码更加健壮优雅,还是要多使用一些的!当然不能过渡使用!!

对你有帮助,还请不要吝啬你的发财小手点点关注哈!、
写作不易,大家给点支持,你的支持是我写作的动力哈!

关注小编的微信公众号,一起交流学习!文章首发看哦!

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

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

相关文章

华为OD机试模拟题 用 C++ 实现 - 最优资源分配(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 最多获得的短信条数(2023.Q1)) 文章目录 最近更新的博客使用说明最优资源分配题目输入输出描述备注示例一输入输出说明示例二输入输出说明Code使用说明 参加华为od机试,一定要注

2023 Java 分布式 面试 大纲

前段时间 &#xff0c;公司部门的HR找到我&#xff0c;说来了几份简历 &#xff0c;都是三年所有的开发 让我面一下&#xff0c; HR那边 一面核对了基本的信息 二面技术&#xff0c;是由我来接手&#xff0c;然后问了 一些分布式的问题 &#xff0c;大部分都是在围绕着SpringCl…

这回稳了!电力巡检低功耗摄像头全新来袭

最近的狂飙成为大家的话题&#xff0c;互联网的发展让很多信息都很透明&#xff0c;这个也是我比较喜欢和各位技术大咖一起分享一些当下比较前沿的解决方案 春回大地&#xff0c;疫情远去&#xff0c;我们也没有理由逃避不去努力&#xff0c;在互相网的各种平台去获取各种自己需…

安全配置检查的必要性?以及检查流程

随着行业信息化建设的不断深入&#xff0c;生产、业务支撑系统的网络结构越来越复杂&#xff0c;由此带来的各种应用和服务器的数量及种类也日益增多&#xff0c;一旦发生维护人员错误操作&#xff0c;或者采用一成不变的初始系统设置&#xff0c;就可能会带来安全隐患&#xf…

〖大前端 - 基础入门三大核心篇②〗- 前端开发工具和环境准备

大家好&#xff0c;我是 哈士奇 &#xff0c;一位工作了十年的"技术混子"&#xff0c; 致力于为开发者赋能的UP主, 目前正在运营着 TFS_CLUB社区。 &#x1f4ac; 人生格言&#xff1a;优于别人,并不高贵,真正的高贵应该是优于过去的自己。&#x1f4ac; &#x1f4e…

2020蓝桥杯真题跑步锻炼(填空题) C语言/C++

题目描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 小蓝每天都锻炼身体。 正常情况下&#xff0c;小蓝每天跑 1 千米。如果某天是周一或者月初&#xff08;1 日&#xff09;&#xff0c;为了激励自己&#xff0c;小蓝…

TCP协议原理三

文章目录七、延时应答八、捎带应答九、面向字节流粘包问题十、TCP异常情况总结七、延时应答 如果说滑动窗口的关键是让窗口大一些&#xff0c;传输速度就快一些。那么延时应答就是在接收方能够处理的前提下&#xff0c;尽可能把ack返回的窗口大小尽可能大一些。 如果在接受数据…

关于事务的理解

事务的概念 事务处理几乎是每一个信息系统中都会涉及到的问题&#xff0c;它存在的意义就是保证系统中的数据是正确的&#xff0c;不同数据间不会产生矛盾&#xff0c;也就是保证数据状态的一致性&#xff08;Consistency&#xff09;。 关于一致性&#xff0c;我们重点关注的…

MySQL —— 基本查询

文章目录1. 向表中插入数据2. 查询操作2.1 全列查询2.2 指定列查询2.3 查询字段带表达式2.4 为查询结果指定别名2.5 去重操作3. where 条件3.1 比较运算符和逻辑预算符的运用3.2 like的细节3. 3 null查询4. 对查询的结果进行排序4.1 对单一字段进行排序4.2 对多个字段排序4.3 对…

密码学基础概念

把一段原始数据通过某种算法处理成另外一种数据&#xff08;原始数据为明文&#xff0c;处理后的数据为密文&#xff09;。明文->密文&#xff1a;称之为加密。密文->明文&#xff1a;称之为解密。 在加密过程中我们需要知道下面的这些概念&#xff1a; 1&#xff09;明文…

操作系统——10.进程通信

这篇文章我们来讲一下进程通信的相关内容 目录 1.概述 2.什么是进程通信 2.1进程通信——共享存储 2.2进程通信——管道通信 2.3进程通信——消息传递 3.小结 1.概述 首先&#xff0c;我们来看一下这节内容的大体框架 2.什么是进程通信 顾名思义&#xff0c;进程通信就是…

QML 鼠标事件

作者: 一去、二三里 个人微信号: iwaleon 微信公众号: 高效程序员 QML 中有一些元素本身是不具备交互能力的(例如:Rectangle、Text、Image 等),那么如何通过鼠标来控制它们的行为呢?这里就需要用到 MouseArea 元素了,它继承于 Item 且不可见,通常需要与可见元素结合使…

【vue2小知识】路由守卫的使用与解决RangeError: Maximum call stack size exceeded问题的报错。

&#x1f973;博 主&#xff1a;初映CY的前说(前端领域) &#x1f31e;个人信条&#xff1a;想要变成得到&#xff0c;中间还有做到&#xff01; &#x1f918;本文核心&#xff1a;当我们在路由跳转前与后我们可实现触发的操作 【前言】当我们在做类似于登录页面的时候&…

jmeter-如何在多线程一起执行时,控制请求的执行顺序【临界部分控制器】

前言&#xff1a;一个线程多个脚本的时候&#xff0c;发现只要100个用户同时执行&#xff0c;请求就会乱。期望2个线程执行结果&#xff1a;获取验证码-注册-登录这个流程获取验证码-注册-登录这个流程实际2个线程执行结果&#xff1a;a. 登录-获取验证码-注册b. 注册-获取验证…

运动无线蓝牙耳机哪款好、运动无线蓝牙耳机推荐

作为 运动爱好者&#xff0c;每天早晨醒来后的第一件事就去家门口的湿地公园跑上一圈。各种运动装备都齐了&#xff0c;不过在耳机选择上还真的犯难&#xff0c;打着“运动耳机”旗号的产品也是种类繁多&#xff0c;那么到底什么样的无线耳机更适合运动呢&#xff1f;于是我花时…

零基础入门网络安全,看这一篇就够了!

前景 很多零基础朋友开始将网络安全作为发展的大方向&#xff0c;的确&#xff0c;现如今网络安全已经成为了一个新的就业风口&#xff0c;不仅大学里开设相关学科&#xff0c;连市场上也开始大量招人。 那么网络安全到底前景如何&#xff1f;大致从市场规模、政策扶持、就业…

vxe-table 表格多选框回显

1.弹框表格结构 <a-modal v-if"visibleQuality" title"请选择需要提高的能力素质要求" :maskClosable"false" :visible"visibleQuality && switchStatus" ok"handleOkQuality" cancel"handleCancelQuality&…

【微信小程序-原生开发+TDesign】通用功能页封装——地点搜索(含腾讯地图开发key 的申请方法)

效果预览 核心技能点 调用腾讯地图官方的关键字地点搜索功能&#xff0c;详见官方文档 https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/methodGetsuggestion 完整代码实现 地点输入框 <t-input value"{{placeInfo.title}}" bindtap"searchPlace" dis…

华为OD机试模拟题 用 C++ 实现 - 端口合并(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 最多获得的短信条数(2023.Q1)) 文章目录 最近更新的博客使用说明端口合并题目输入输出示例一输入输出说明示例二输入输出说明示例三输入输出说明

力扣299.猜数字游戏(java语言实现)

题目描述&#xff1a; 你在和朋友一起玩 猜数字&#xff08;Bulls and Cows&#xff09;游戏&#xff0c;该游戏规则如下&#xff1a; 写出一个秘密数字&#xff0c;并请朋友猜这个数字是多少。朋友每猜测一次&#xff0c;你就会给他一个包含下述信息的提示&#xff1a; 猜测…