一、前言

我们在开发中最常见的异常就是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<U>> 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的话,那么这个问题能够得到避免。

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

对你有帮助,还请不要吝啬你的发财小手点点关注哈!、

写作不易,大家给点支持,你的支持是我写作的动力哈!

关注小编的微信公众号:『小王博客基地』,一起交流学习!文章首发看哦!

建了一个IT交流群,欢迎大家加入,过期加我拉你们进哈!

告别空指针让代码变优雅,Optional使用图文例子源码解读的更多相关文章

  1. 聊一聊Java8 Optional,让你的代码更加优雅

    码农在囧途 随着时间的推移,曾经我们觉得重要的东西,可能在今天看来是如此的浅薄和无知,同理,今天我们放不下,想不开,觉得重要的东西,多年后我们可能也会觉得也就那样,所以,今天的的所有烦恼,忧愁,想不开 ...

  2. 还在重复写空指针检查代码?考虑使用 Optional 吧!

    一.前言 如果要给 Java 所有异常弄个榜单,我会选择将 NullPointerException 放在榜首.这个异常潜伏在代码中,就像个遥控炸弹,不知道什么时候这个按钮会被突然按下(传入 null ...

  3. JDK8漫谈——代码更优雅

    简介 lambda表达式,又称闭包(Closure)或称匿名方法(anonymous method).将Lambda表达式引入JAVA中的动机源于一个叫"行为参数"的模式.这种模式 ...

  4. CSS 黑魔法小技巧,让你少写不必要的JS,代码更优雅

    首页   登录注册         CSS 黑魔法小技巧,让你少写不必要的JS,代码更优雅 阅读 8113 收藏 927 2017-09-26 原文链接:github.com 腾讯云容器服务CSS,立 ...

  5. 用AOP来让你的JS代码变得更有可维护性吧

    此文已由作者吴佳祥授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 好吧我承认这是篇任务. 最近看到个消息,ES2017已经定稿了,心想,我去,还完全没了解ES2016呢,ES ...

  6. 【原创】基于.NET的轻量级高性能 ORM - TZM.XFramework 之让代码更优雅

    [前言] 大家好,我是TANZAME.出乎意料的,我们在立冬的前一天又见面了,天气慢慢转凉,朋友们注意添衣保暖,愉快撸码.距离 TZM.XFramework 的首秀已数月有余,期间收到不少朋友的鼓励. ...

  7. Lambda表达式, 可以让我们的代码更优雅.

    在C#中, 适当地使用Lambda表达式, 可以让我们的代码更优雅. 通过lambda表达式, 我们可以很方便地创建一个delegate: 下面两个语句是等价的 Code highlighting p ...

  8. C# 多线程的坑 之 代码变序

    英文好的,可跳过,直接打开底部的“参考“链接. 代码变序--reordering of memory operations 大概4年前,阅读了这篇文章后http://www.albahari.com/ ...

  9. JAVA8-让代码更优雅之List排序

    先定义一个实体类 @Data @AllArgsConstructor @NoArgsConstructor public class Human { private String name; priv ...

  10. 【如何让代码变“高级”(二)】-这样操作值得一波666(Java Stream)(这么有趣)

    [如何让代码变“高级”(二)]-这样操作值得一波666(Java Stream)(这么有趣) 开发中的代码 在开发中的代码是不是很常见这样的代码: 这样的? for循环取元素取值 List<Us ...

随机推荐

  1. 【FAQ】关于CP反馈的联运应用的常见结算问题小结

    问题一:为什么在"我的账户">>"收益"里面的金额和支付报表中的金额对不上 ? 关于联运类应用付费产品在华为平台上结算问题,您可以详细参考一下&qu ...

  2. 知识图谱之《海贼王-ONEPICE》领域图谱项目实战(含码源):数据采集、知识存储、知识抽取、知识计算、知识应用、图谱可视化、问答系统(KBQA)等

    知识图谱之<海贼王-ONEPICE>领域图谱项目实战(含码源):数据采集.知识存储.知识抽取.知识计算.知识应用.图谱可视化.问答系统(KBQA)等 实体关系可视化页面可视化页面尝鲜 1. ...

  3. 【VUE】 文件预览

    [VUE] 文件预览 上传前预览 word文档:docx.doc 核心代码 import {renderAsync} from "docx-preview"; /** * 渲染do ...

  4. Prompt Playground: 一个简易的提示词调试工具

    Prompt Playground: 一个简易的提示词调试工具 将LLM引入到日常的开发工作中后,会面临大量的提示词调试的工作,由于LLM不确定性,这个工作会变得非常的繁琐,需要不断的调整,甚至需要大 ...

  5. 王道oj/problem16

    网址:http://oj.lgwenda.com/problem/16 思路:都在注释里,注意增删查的参数以及停止条件 代码: #define _CRT_SECURE_NO_WARNINGS#incl ...

  6. Gin+Xterm.js实现远程Kubernetes Pod(一)

    Xterm.js简介 xterm.js (https://xtermjs.org/)是一个开源的 JavaScript 库,它模拟了一个终端接口,可以在网页中嵌入一个完全功能的终端.这个库非常灵活,并 ...

  7. 【算法】编写一个函数,返回两个正数的和,有可能超过ulong长度

    编写一个函数,返回两个数字的和.输入数字是字符串,函数必须返回一个字符串. 示例: 添加("123","321"):->"444" 添 ...

  8. 【技术积累】Linux中的命令行【理论篇】【九】

    blkid命令 命令介绍 blkid命令是一个用于查看块设备属性的Linux命令.它可以识别和显示块设备的文件系统类型.UUID.LABEL.PARTUUID等信息. 命令说明 在Linux下可以使用 ...

  9. cockpit--一款开源的适用于单主机的Linux监控面板

    在搜索Linux监控时,偶然发现一款还不错的监控面板,该面板为red hat开发,适用于各种Linux发行版,部署也非常方便,官方文档Running Cockpit - Cockpit Project ...

  10. SpringMVC配置web.xml文件详解(列举常用的配置)

    常用的web.xml的配置 1.Spring 框架解决字符串编码问题:过滤器 CharacterEncodingFilter(filter-name) 2.在web.xml配置监听器ContextLo ...