我患上了空指针后遗症!

风筝 2024-03-22 15:21:00
下面这个报错,相信没有任何一个 Java 程序员没有被它折磨过。我们对他的熟悉程度简直超过了 Hello World。图片何止是熟悉,那简直是深恶痛绝,以至于我对它都产生了后遗症。

 

图片

 

每当本地调试出现这个错误的时候,都恨不得掐一下大腿,然后默默的对自己说:垃圾,还犯这么愚蠢的错误呢?

 

不知道有多少同学和我一样有这种感受呢?

 

回想起我之前接手的一个项目,线上出现了问题,当我到了服务器一看日志,只有几个单词,那就是 java.lang.NullPointerException,那一刻我是头晕目眩,差点一头撞在 27 寸的显示器上。回想上一次出现这种症状,还是几年前挤早高峰的公交车,挤得我双脚离地,外加有点低血糖。

 

当然主要问题并不是 NPE(NullPointerException),还是要仰仗前辈异常处理的“非常优秀”,异常包裹的严严实实的,只留了java.lang.NullPointerException这一点点信息。

 

于是只能打开代码,找到报错的接口,一步步排查,满眼看去,皆可空指针啊。从此之后,空指针异常给我留下了深深的阴影。

 

好在从 JDK 14之后,NPE 异常不再仅仅是简单的这几个单词了,而会附带更加具体的异常信息,比如对一个赋值为 null 的字符串求长度,能捕捉到下面这样的异常信息:

 

  •  
Cannot invoke "String.length()" because "s" is null

 

空指针的由来

 

要说空指针异常,那还不只是 Java 的问题,绝大多数语言都有这个问题,比如 C++、C#、Go,但是也有没有这个问题,比如 Rust 。

 

空指针最早是编程界的鼻祖级人物 Tony Hoare 引入的,早在 1965年,他设计 ALGOL 60 语言的时候引入了Null 的设计,ALGOL 可谓是 C 语言的祖宗。ALGOL 中的 Null 被后来的众多语言设计者引入,就包括前面提到的这些语言。

 

Tony Hoare 不仅发明了我们熟悉的 Null,还是令众多算法残废闻风丧胆的快速排序算法(Quick Sort)的发明者,这个算法也是当前世界上使用最广泛的算法之一。

 

 

图片

 

空指针后遗症.png

 

2009年3月他在Qcon技术会议上发表了题为「Null引用:代价十亿美元的错误」的演讲,回忆自己1965年设计第一个全面的类型系统时,未能抵御住诱惑,加入了Null引用,仅仅是因为实现起来非常容易。它后来成为许多程序设计语言的标准特性,导致了数不清的错误、漏洞和系统崩溃,可能在之后40年中造成了十亿美元的损失。

 

如何应对空指针

 

处理空指针有一些措施,我们常常称之为「防御式编程」,这个说法也很形象,你不防着它,它真的就上来伤害你。

 

 
1、主动检查空指针,要使用一个变量之前,要检查这个变量是不是空,不是空再操作,比如常用的对字符串判空

 

  •  
  •  
  •  
public static boolean isEmpty(CharSequence cs) { return cs == null || cs.length() == 0;}

 

对应的很多字符串工具类都有 isEmpty、isNotEmpty、isNotBlank 这种方法。

 

 

图片


 

同样的,还有对于集合的判断,好多工具包都有 CollectionUtil.isEmpty这样的方法。

 

 

图片


 

为了避免空引用异常,有时候我们写的代码可能像下面这个样子,一步一判空。这样可以提高代码的健壮性和可靠性,但是看上去并不是很美观。

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
public static String getUserOrderDetail(Integer userId) { User user = User.getUser(userId); if (user != null) {  Order order = user.getOrder();  if (order != null) {   Address address = order.getAddress();   if (address != null) {    String detail = address.getDetail();    if (detail != null) {     return detail;    }   }  } } return "不好意思,找了半天,没找到";}

 

还好,Java 8 中引入的 Optional 类可以简化这个流程。

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
public static String getUserOrderDetail(Integer userId) { return Optional.ofNullable(User.getUser(userId))   .map(User::getOrder)   .map(Order::getAddress)   .map(Address::getDetail)   .orElse("不好意思,找了半天,没找到");}

 

 
2、 能不返回 NULL 的话,就尽量不返回 NULL

 

比如有些获取集合的方法,没有结果的话,可以返回一个空列表。这种方式对于提供给前端或者消费者使用的接口更加适用,返回一个空集合要远比返回一个空更友好。

 

 
3、 能抛异常的话,宁可抛异常,也不要返回 NULL

 

还有一些情况,抛出给调用者一个具体的异常,要比返回一个 NULL 更加能让调用者清楚到底发生了什么。

 

比如根据一个用户的信息,但是发现用户不存在了,直接返回给调用者一个「用户不存在」的异常信息更明确,而不是返回一个 NULL,让调用方去猜。

 

作者丨风筝
来源丨公众号:古时的风筝(ID:gushidefengzheng)
dbaplus社群欢迎广大技术人员投稿,投稿邮箱:editor@dbaplus.cn
最新评论
访客 2023年08月20日

230721

访客 2023年08月16日

1、导入Mongo Monitor监控工具表结构(mongo_monitor…

访客 2023年08月04日

上面提到: 在问题描述的架构图中我们可以看到,Click…

访客 2023年07月19日

PMM不香吗?

访客 2023年06月20日

如今看都很棒

活动预告