避之不及的 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("李四"));

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

  1. Java中的记录器 - Java日志示例

    Java中的记录器 - Java日志示例 今天我们将研究Java中的Logger.Java Logger提供了java编程的日志记录. 目录[ 隐藏 ] 1 Java中的记录器 1.1 Java Lo ...

  2. Java中的正则表达式 - Java Regex示例

    Java中的正则表达式 - Java Regex示例 欢迎使用Java中的正则表达式.它在Java中也称为Regex.当我开始编程时,java正则表达式对我来说是一场噩梦.本教程旨在帮助您掌握Java ...

  3. java中 数组声明,java数组声明格式

    java 声明动态数组,java对象数组详解,java中声明数组,java数组声明格式 Java 中数组的声明一维数组的声明: 在 Java 中,数组是独立的对象,有自身的方法,不是变量的集合. 数组 ...

  4. Java中apple导入那个包_在Java中,由Java编泽器自动导入而无需在程序中用import导入的包是()。A.java.appletB.java.awtC.j...

    在Java中,由Java编泽器自动导入而无需在程序中用import导入的包是().A.java.appletB.java.awtC.j 更多相关问题 问卷星是一个专业.无限制的免费在线问卷调查.测评. ...

  5. java中properties作用,java中Properties类的使用

    java中Properties类的使用 在java.util 包下面有一个类 Properties,该类主要用于读取以项目的配置文件(以.properties结尾的文件和xml文件). Propert ...

  6. java中使用队列:java.util.Queue

    在java5中新添加了java.util.Queue接口,用以支持队列的常见操作.该接口扩展了java.util.Collection接口. Queue使用时要尽量避免Collection的add() ...

  7. Java中的泛型 --- Java 编程思想

    前言 ​ 我一直都认为泛型是程序语言设计中一个非常基础,重要的概念,Java 中的泛型到底是怎么样的,为什么会有泛型,泛型怎么发展出来的.通透理解泛型是学好基础里面中非常重要的.于是,我对<Ja ...

  8. java中strictfp关键字,java strictfp关键字用法大全详解

    一.strictfp关键字简介 strictfp是Java中提供的一个保留关键字,该关键字是从这第java JDK2版本儿开始出现的一直沿用到现在,只不过很多情况下都不怎么使用,所以容易被大家遗忘,因 ...

  9. java中flush 函数,Java DataOutputStream.flush()类型

    DataOutputStream(OutputStream out)构造函数 DataOutputStream的DataOutputStream.flush()具有以下语法. public void ...

最新文章

  1. Y Combinator
  2. Java创建线程的3种方式
  3. js 引用 java常量_java调用JS 与JS 调java
  4. Java学习笔记11-2——Spring5
  5. 感想总结——热烈庆祝CSDN博客排名进入前20000名
  6. webp环境搭建和使用过程
  7. Atitit java集成内嵌浏览器与外嵌浏览器attilax总结
  8. UML之用例图箭头方向
  9. 爬虫 - scrapy框架设置代理
  10. 第六章 函数逼近-强化学习理论学习与代码实现(强化学习导论第二版)
  11. 阿里云盘下载安装保存文件教程
  12. Windows无法连接到打印机、打印机连接出现0X00000bcb错误应该如何解决?这应该是是最全面的解决方法啦~~
  13. java利用梦网云通讯发送短信
  14. PS暂存盘已满怎么办
  15. MATLAB三维画图函数使用总结
  16. Microsoft 文本转语音应用
  17. 关于使用媒体查询@meda失效原因的总结或注意事项
  18. abb机器人码垛编程详解_ABB机器人码垛编程方法与技巧
  19. java无法验证发布者_Win10弹出无法验证发布者怎么解决?
  20. web安全测试用例(网络资源笔记)

热门文章

  1. 【渝粤教育】 国家开放大学2020年春季 2528监督学 参考试题
  2. 【渝粤教育】电大中专新媒体营销实务 (11)作业 题库
  3. 成都亿佰特物联网无线数传专家:lora无线传输模块网关技术的优缺点
  4. python中正则表达式是什么意思_python – 正则表达式中[^.] *的含义是什么?
  5. python写入数据到excel_python实现查询的数据写入到excel
  6. having和where可以同时使用吗_阴、阳离子聚丙烯酰胺可以同时混合溶解使用吗?...
  7. 计算机导论设计实验,基于抽象知识点的《计算机导论》实验软件设计
  8. java测试netty_《Netty官方文档》基准测试
  9. go语言查询某个值是否在数组中_go语言中的数组
  10. python 档案管理系统_Python 写入档案的 4 个方法