java中npe问题,【Java 8】巧用Optional之优雅规避NPE问题

避之不及的 NullPointerException

NPE : NullPointerException

空指针异常是最常见的Java异常之一,抛出NPE错误不是用户操作的错误,而是开发人员的错误,应该被避免,那么只能在每个方法中加入非空检查,阅读性和维护性都比较差。

以下是一个常见的嵌套对象:一个用户所拥有的汽车,以及为这个汽车配备的保险。

public class User {

private String userName;

private Car car;

public String getUserName() {

return userName;

}

public void setUserName(String userName) {

this.userName = userName;

}

public Car getCar() {

return car;

}

public void setCar(Car car) {

this.car = car;

}

}

public class Car {

private String carName;

private Insurance insurance;

public String getCarName() {

return carName;

}

public void setCarName(String carName) {

this.carName = carName;

}

public Insurance getInsurance() {

return insurance;

}

public void setInsurance(Insurance insurance) {

this.insurance = insurance;

}

}

public class Insurance {

private String insuranceName;

public String getInsuranceName() {

return insuranceName;

}

public void setInsuranceName(String insuranceName) {

this.insuranceName = insuranceName;

}

}

如果我们此时,需要获取一个用户对应的汽车保险名称,我们可能会写出来以下的代码

private String getInsuranceName(User user) {

return user.getCar().getInsurance().getInsuranceName();

}

显然上面的程序是存在诸多NullPointerException隐患的,为了保证程序的健壮性,我们需要尽量避免出现空指针NullPointerException,那么通常我们会有以下两种写法。

深层质疑

private String getInsuranceName(User user) {

if (user != null) {

Car car = user.getCar();

if (car != null) {

Insurance insurance = car.getInsurance();

if (insurance != null) {

return insurance.getInsuranceName();

}

}

}

return "not found";

}

及时退出

private String getInsuranceName(User user) {

if (user == null) {

return "not found";

}

Car car = user.getCar();

if (car == null) {

return "not found";

}

Insurance insurance = car.getInsurance();

if (insurance == null) {

return "not found";

}

return insurance.getInsuranceName();

}

为了避免出现空指针,我们通常会采用以上两种写法,但是它们复杂又冗余,为了鼓励程序员写更干净的代码,代码设计变得更加的优雅。JAVA8提供了Optional类来优化这种写法。

Optional

Java 8中引入了一个新的类java.util.Optional。这是一个封装Optional值的类。举例来说,使用新的类意味着,如果你知道一个人可能有也可能没有车,那么User类内部的car变量就不应该声明为Car, 遇某人没有车时把null引用值给它,而是应该如下图所示直接将其声明为Optional类型。

变量存在时,Optional类只是对类简单封装。变量不存在时,缺失的值会被建模成一个“空” 的Optional对象,由方法Optional.empty()返回。它返回Optional类的特定单一实例。

null引用和Optional.empty() 有什么本质的区别吗?

从语义上,你可以把它们当作一回事儿,但是实际中它们之间的差别非常大:如果你尝试直接引用一个null,一定会触发NullPointerException,不过使用 Optional.empty()就完全没事儿,它是Optional类的一个有效对象。

使用Optional而不是null的一个非常重要而又实际的语义区别是,第一个例子中,我们在声明变量时使用的是Optional类型,而不是Car类型,这句声明非常清楚地表明了这里发生变量缺失是允许的。与此相反,使用Car这样的类型,可能将变量赋值为null,你只能依赖你对业务模型的理解,判断一个null是否属于该变量的有效值又或是异常情况。

public class User {

private String userName;

private Optional car;

public String getUserName() {

return userName;

}

public Optional getCar() {

return car;

}

}

public class Car {

private String carName;

private Optional insurance;

public String getCarName() {

return carName;

}

public Optional getInsurance() {

return insurance;

}

}

public class Insurance {

private String insuranceName;

public String getInsuranceName() {

return insuranceName;

}

}

发现Optional是如何 富你模型的语义了吧。代码中user引用的是Optional, 而car引用的是Optional,这种方式非常清晰地表达了你的模型中一个user 可能有也可能没有car的情形,同样,car可能进行了保险,也可能没有保险。

与此同时,我们看到insurance的名称insuranceName被声明成String类型,而不是Optional ,这非常清楚地表明声明为insurance的类中的名称字段insuranceName是必须存在的。

使用这种方式, 一旦通过引用insurance获取insuranceName时发生NullPointerException,你就能非常确定地知道出错的原因,不再需要为其添加null的检查查,因为null的检查查只会掩盖问题,并未真正地修复问题。

insurance必须有个名字,所以,如果你遇到一个insurance没有名称,你需要调查你的数据出了什么问题,而不应该再添加一段代码,将这个问题隐藏。

Optional的方法介绍

创建Optional

of(T value)

如果构造参数是一个null,这段代码会立即 出一个NullPointerException,而不是等到你 图访问car的属性值时才返回一个错误。

public static Optional of(T value) {

return new Optional<>(value);

}

ofNullable(T value)

创建一个允许null值的Optional对象

public static Optional ofNullable(T value) {

return value == null ? empty() : of(value);

}

empty()

创建一个空的Optional对象

public static Optional empty() {

Optional t = (Optional) EMPTY;

return t;

}

常用方法get()是这些方法中最简单但又最不安全的方法。如果变量存在,它直接返回封装的变量值,否则就抛出一个NoSuchElementException异常。所以,除非你非常确定Optional变量一定包含值,否则最好不要使用这个方法。

orElse(T other),它允许你在 Optional对象不包含值时提供一个默认值。

orElseGet(Supplier extends T> other)是orElse方法的延迟调用版,Supplier方法只有在Optional对象不含值时才执行调用。

orElseThrow(Supplier extends X> exceptionSupplier)和get方法非常类似,它们遭遇Optional对象为空时都会抛出一个异常,但是使用orElseThrow你可以定制希望抛出的异常类型。

ifPresent(Consumer super T>)让你能在变量值存在时执行一个作为参数传入的方法,否则就不进行任何操作。

注意:orElse(T other)和orElseGet(Supplier extends T> other)的区别

这两个函数的区别:当value值不为null时,orElse函数依然会执行返回T的方法,而orElseGet函数并不会执行返回T的方法。

用map从Optional中提取和转换值

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

可以把Optional对象看成一种特殊的集合数据,它至多包含一个元素。如果Optional包含一个值,那函数就将该值作为参数传递给map,对该值进行转换。如果Optional为空,就什么也不做。

String optionMap = Optional.ofNullable("abc").map(value -> value.toUpperCase()).get();

使用flatMap链接Optional对象

flatMap(Function super T, Optional> mapper)

将两层的optional合并为一个

String optionFlatMap = Optional.ofNullable("abc").flatMap(value -> Optional.of((value + "flat-map").toUpperCase())).get();

用filter剔除特定的值

filter(Predicate super T> predicate)

filter方法接受一个谓词作为参数。如果Optional对象的值存在,并且它符合谓词的条件, filter方法就返回其值;否则它就返回一个空的Optional对象。

Optional filterOptional = Optional.ofNullable("abc").filter(value -> Objects.equals(value, "abc"));

实战

尝试获取用户的用户名称,不存在则返回默认值

String userName = Optional.ofNullable(userOfNull).orElse(new User()).getUserName();

尝试获取用户的carName,不存在则返回null

String carName = Optional.ofNullable(userOfNull).map(u -> u.getCar()).map(c -> c.getCarName()).orElse(null);

用户名存在的时候转为大写

Optional.ofNullable(user).map(u -> u.getUserName()).ifPresent(userName -> System.out.println(userName.toUpperCase()));

过滤出来用户名称是张三的用户

Optional.ofNullable(user).filter(u -> Objects.equals(u.getUserName(),"张三")).map(u -> u.getUserName()).ifPresent(userName -> System.out.println(userName + "实战Test"));

将张三的用户名称更改为李四

Optional.ofNullable(user).ifPresent(x -> {

if (Objects.equals(user.getUserName(),"张三")){

user.setUserName("李四");

}

});

Optional.ofNullable(user).filter(u -> Objects.equals(user.getUserName(),"张三")).ifPresent(x -> user.setUserName("李四"));

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

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

相关文章

adf435编程_动态ADF列车:以编程方式添加列车停靠点

adf435编程我将展示如何以编程方式“即时”将火车停靠站添加到ADF火车中。 在我的用例中&#xff0c;我有一些票务预订应用程序。 它具有训练模型的有限任务流。 在火车的第一站&#xff0c;用户输入乘客数量&#xff0c;在随后的站点&#xff0c;他们输入一些乘客信息。 必须根…

JAVA消息服务JMS规范及原理详解

一、简介 JMS即Java消息服务&#xff08;Java Message Service&#xff09;应用程序接口&#xff0c;是一个Java平台中关于面向消息中间件&#xff08;MOM&#xff09;的API&#xff0c;用于在两个应用程序之间&#xff0c;或分布式系统中发送消息&#xff0c;进行异步通信。Ja…

php mk的支持扩展,Linux部署Redis及PHP-redis扩展

Redis是一个key-value存储系统&#xff0c;属于我们常说的NoSQL。它遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库&#xff0c;并提供多种语言的API。它常用在缓存、队列、Pub/Sub、计数统计、排行榜、投票与排序等场景。本文介绍如何在CentOS7上安装…

定制Hamcrest Matchers

本文是我们名为“ 用Mockito进行测试 ”的学院课程的一部分。 在本课程中&#xff0c;您将深入了解Mockito的魔力。 您将了解有关“模拟”&#xff0c;“间谍”和“部分模拟”的信息&#xff0c;以及它们相应的Stubbing行为。 您还将看到使用测试双打和对象匹配器进行验证的过…

Fiddle用于移动端抓包

一、什么情况下可以用到 1、调查参考其他移动端网站的抓包&#xff0c;他们传输方式。如微信上京东的智能机器人的包、移动端的请求接口格式、如何实现的效果等。 2、调试本地移动端页面的测试页面效果是否有问题。如:页面的样式、js、结构等。 二、操作(http) a、安装猎豹免费…

php中得到复选框的数据的代码,表单复选框向PHP传输数据的代码

表单复选框向PHP传输数据的代码表单复选框就是checkbox1、checkbox的应用复制代码 代码如下:2、由于我传输的是在php循环中产生的数组&#xff0c;因此value也要设成变量&#xff1a;for($i0;$i<10;$i){?>">]" value"要传输的内容">]" v…

ubuntu14.04不能安全卸载移动硬盘

ubuntu14.04不能安全卸载移动硬盘 sudo udisksctl unmount -b /dev/sdb1 udisksctl power-off -b /dev/sdb hdparm -Y /dev/sdb 转载于:https://www.cnblogs.com/fedorayang/p/6563552.html

Kioptrix-2

环境搭建 这个靶场环境稍微有点麻烦&#xff0c;首次打开的时候&#xff0c;需要将靶机从VM中移除&#xff08;注意是 从VM里面移除&#xff09;&#xff0c;然后利用nodpad等工具打开vmx文件&#xff0c;然后两步&#xff1a; 所有以“ethernet0”开头的条目并保存更改。然后…

php 递归到空如何处理,php递归调用删除数组空值元素的方法

这篇文章主要介绍了php递归调用删除数组空值元素的方法,涉及php递归调用操作数组的相关技巧,非常具有实用价值,需要的朋友可以参考下本文实例讲述了php递归调用删除数组空值元素的方法。分享给大家供大家参考。具体如下&#xff1a;该函数可以删除数组里的所有空值元素&#xf…

lnmp pathinfo问题

location / { if (!-e $request_filename) {rewrite ^/(.*)$ /index.php/$1 last;break;}}location ~ \.php {fastcgi_pass 127.0.0.1:9000; //这里不用担心你的服务器的ip是否和这个不一样&#xff0c;不用怀疑&#xff0c;//这样写就行了fastcgi_index index.php;…

maven配置testng_TestNG和Maven配置指南

maven配置testng为了有用&#xff0c;自动测试应该运行得非常快。 否则&#xff0c;将不会在开发期间经常运行&#xff0c;甚至在开发人员工作站上的默认配置中将被忽略。 最简单的规则是只编写小型单元测试&#xff0c;该测试将模拟给定类的邻居。 然而&#xff0c;有时在IoC容…

oracle00011,oracle11g 导出表报EXP-00011:table不存在。

oracle11g&#xff0c;在用exp命令备份数据库时&#xff0c;如果表中没有数据报EXP-00011错误&#xff0c;对应的表不存在。这导致对应的空表无法备份。原因&#xff1a;11g默认创建一个表时不分配segment&#xff0c;只有在插入数据时才会产生(当然也可以强制分配)&#xff0c…

《中国文化要略》第八章 古代教育 第九章 科举制度

转载于:https://www.cnblogs.com/bgd140206319/p/6565654.html

Hamcrest Matchers教程

本文是我们名为“ 用Mockito进行测试 ”的学院课程的一部分。 在本课程中&#xff0c;您将深入了解Mockito的魔力。 您将了解有关“模拟”&#xff0c;“间谍”和“部分模拟”的信息&#xff0c;以及它们相应的Stubbing行为。 您还将看到使用测试双打和对象匹配器进行验证的过…

oracle 回滚空间查询,oracle回滚段和回滚表空间操作

1、查询回滚段信息&#xff1a;状态为ONLINE&#xff0c;当前UNDO表空间为undotbs1SQL>select segment_name, owner, tablespace_name, status from dba_rollback_segs;SEGMENT_NAME OWNER TABLESPACE_NAME STATUS------------------------------ ------ ------------------…

win7下安装 python2 和python3

一直纠结于选择py2还是py3&#xff0c;不如在同一系统下安装两个版本就好了。 1、安装python2.7和python3.5 直接到官网https://www.python.org/下载&#xff0c;安装就可以了。 2.安装比较简单&#xff0c;点exe文件一直下一步就可以了&#xff08;注意&#xff1a;安装的时候…

oracle分页查询加总数,oracle count 百万级 分页查询记要总数、总条数优化

oracle count 百万级 分页查询记录总数、总条数优化oracle count 百万级 查询记录总数、总条数优化最近做一个项目时&#xff0c;做分页时&#xff0c;发现分页查询速度很慢&#xff0c;分页我做的是两次查询&#xff0c;一次是查询总数&#xff0c;一次是查询分页结果/** 查询…

mysql为字段值添加或者去除前缀、后缀(查询字段拼值)

添加前缀update ecs_goods set goods_nameconcat(新中式,goods_name) where cat_id 4; 添加后缀update ecs_goods set goods_nameconcat(goods_name,新中式) where cat_id 4; 删除 update ecs_goodsset goods_nameright(goods_name,length(goods_name)-1) where cat_id 4; 其中…

maven使用testng_使用ReportNG更好看的TestNG HTML测试报告– Maven指南

maven使用testng当“扩展TestCase”是编写测试中必不可少的部分时&#xff0c; TestNG是作为JUnit 3的注释驱动替代创建的测试框架。 即使到现在&#xff0c;它也提供了一些有趣的功能&#xff0c;例如数据提供程序&#xff0c;并行测试或测试组。 在我们的测试不是从IDE执行的…

谈谈你对oracle的认识,对Oracle存储过程的几点认识

1、写Oracle存储过程时最好不要在其中写Commit语句。一般调用程序会自动Commit数据&#xff0c;比如用NHibernate调用的时候&#xff0c;NHibernate就会自动Commi1、写Oracle存储过程时最好不要在其中写Commit语句。一般调用程序会自动Commit数据&#xff0c;&#xff0c;比如用…