首页 文章详情

我患上了空指针后遗症

Java后端技术 | 68 2024-06-06 08:22 0 0 0
UniSMS (合一短信)
    

往期热门文章:

        

1、这10个小技巧让你减少80%的Bug! 2、升级 JDK17 一个不可拒绝的理由 3、SQL中为什么不要使用1=1? 4、同事使用 insert into select 迁移数据,开开心心上线,导致公司损失近10w! 5、拒绝写重复代码,试试这套开源的SpringBoot组件,效率翻倍~

下面这个报错,相信没有任何一个 Java 程序员没有被它折磨过。我们对他的熟悉程度简直超过了 Hello World。

ef922f597691a5f7b201bc4aa7e50750.webp

何止是熟悉,那简直是深恶痛绝,以至于我对它都产生了后遗症。 每当本地调试出现这个错误的时候,都恨不得掐一下大腿,然后默默的对自己说:垃圾,还犯这么愚蠢的错误呢? 不知道有多少同学和我一样有这种感受呢? 回想起我之前接手的一个项目,线上出现了问题,当我到了服务器一看日志,只有几个单词,那就是 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)的发明者,这个算法也是当前世界上使用最广泛的算法之一。 f28a15396562ec1da714f75b3d64ee2a.webp空指针后遗症.png 2009年3月他在Qcon技术会议上发表了题为「Null引用:代价十亿美元的错误」的演讲,回忆自己1965年设计第一个全面的类型系统时,未能抵御住诱惑,加入了Null引用,仅仅是因为实现起来非常容易。它后来成为许多程序设计语言的标准特性,导致了数不清的错误、漏洞和系统崩溃,可能在之后40年中造成了十亿美元的损失

如何应对空指针

处理空指针有一些措施,我们常常称之为「防御式编程」,这个说法也很形象,你不防着它,它真的就上来伤害你。 1、主动检查空指针,要使用一个变量之前,要检查这个变量是不是空,不是空再操作,比如常用的对字符串判空。
    public static boolean isEmpty(CharSequence cs) {
 return cs == null || cs.length() == 0;
}
对应的很多字符串工具类都有 isEmpty isNotEmpty isNotBlank 这种方法。 4e55a590b99bc6066c86fbf9f6da5d56.webp同样的,还有对于集合的判断,好多工具包都有 CollectionUtil.isEmpty 这样的方法。 71259c1ce29b102ceb87627d9aa2ae0b.webp为了避免空引用异常,有时候我们写的代码可能想下面这个样子,一步一判空。这样可以提高代码的健壮性和可靠性,但是看上去并不是很美观。
    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,让调用方去猜。
    
        
          往期热门文章:
        
      
    

1、我原以为是个笑话,没想到深挖一下背后还有故事。错怪官方了... 2、Lombok!代码“亚健康”元凶?

3 上班摸鱼神器!标星 17k 的安卓手机投屏工具! 4、Zed,有望打败 VS Code 吗? 5、标星 43.1 K!最火跨平台笔记开源项目 6、融入团队代码风格,代码越写越烂! 7、4 个 GitHub 上令人惊艳的项目! 8、Redis“叛逃”开源,得罪了几乎所有人 9、潜伏开源项目3年,混成核心开发者,竟然只为加一个后门 10、就因为开发环境用了 Docker,就被开除了 e15bacc329c361d05c89744e03587b37.webp
good-icon 0
favorite-icon 0
收藏
回复数量: 0
    暂无评论~~
    Ctrl+Enter