2020年3月17日发布,Java正式发布了JDK 14 ,目前已经可以开放下载。在JDK 14中,共有16个新特性,本文主要来介绍其中的一个特性:JEP 358: Helpful NullPointerExceptions

null何错之有?

对于Java程序员来说,null是令人头痛的东西。时常会受到空指针异常(NullPointerException)的骚扰。相信很多程序员都特别害怕出现程序中出现NPE,因为这种异常往往伴随着代码的非预期运行。

在编程语言中,空引用(Null Reference)是一个与空指针类似的概念,是一个已宣告但其并未引用到一个有效对象的变量。

在Java 1 中就包含了了Null引用和NPE了,但是其实,Null引用是伟大的计算机科学家Tony Hoare 早在1965年发明的,最初作为编程语言ALGOL W的一部分。

1965年,英国一位名为Tony Hoare的计算机科学家在设计ALGOL W语言时提出了null引用的想法。ALGOL W是第一批在堆上分配记录的类型语言之一。Hoare选择null引用这种方式,“只是因为这种方法实现起来非常容易”。虽然他的设计初衷就是要“通过编译器的自动检测机制,确保所有使用引用的地方都是绝对安全的”,他还是决定为null引用开个绿灯,因为他认为这是为“不存在的值”建模最容易的方式。

但是在2009年,很多年后,他开始为自己曾经做过这样的决定而后悔不已,把它称为“一个价值十亿美元的错误”。实际上,Hoare的这段话低估了过去五十年来数百万程序员为修复空引用所耗费的代价。因为在ALGOL W之后出现的大多数现代程序设计语言,包括Java,都采用了同样的设计方式,其原因是为了与更老的语言保持兼容,或者就像Hoare曾经陈述的那样,“仅仅是因为这样实现起来更加容易”。

相信很多Java程序员都一样对null和NPE深恶痛绝,因为他确实会带来各种各样的问题(来自《Java 8 实战》)。如:

  • 它是错误之源。 NullPointerException是目前Java程序开发中最典型的异常。它会使你的代码膨胀。
  • 它让你的代码充斥着深度嵌套的null检查,代码的可读性糟糕透顶。
  • 它自身是毫无意义的。 null自身没有任何的语义,尤其是是它代表的是在静态类型语言中以一种错误的方式对缺失变量值的建模。
  • 它破坏了Java的哲学。 Java一直试图避免让程序员意识到指针的存在,唯一的例外是:null指针。
  • 它在Java的类型系统上开了个口子。 null并不属于任何类型,这意味着它可以被赋值给任意引用类型的变量。这会导致问题, 原因是当这个变量被传递到系统中的另一个部分后,你将无法获知这个null变量最初赋值到底是什么类型。

其他语言如何解决NPE问题

我们知道,出了Java语言外,还有很多其他的面向对象语言,那么在其他的一些语言中,是如何解决NPE的问题的呢?

如在Groovy中使用安全导航操作符(Safe Navigation Operator)可以访问可能为null的变量:

def carInsuranceName = person?.car?.insurance?.name

Groovy的安全导航操作符能够避免在访问这些可能为null引用的变量时发生NullPointerException,在调用链中的变量遭遇null时将null引用沿着调用链传递下去,返回一个null。

其实这个功能曾经考虑过增加一个类似的功能,但是后来又被舍弃了。

另外,在Haskell和Scala也有类似的替代品,如Haskell中的Maybe类型、Scala中的Option[T]。

在 Kotlin 中,其类型系统严格区分一个引用可以容纳 null 还是不能容纳。也就是说,一个变量是否可空必须显示声明,对于可空变量,在访问其成员时必须做空处理,否则无法编译通过:

var a: String = "abc"
a = null // 编译错误

果允许为空,可以声明一个可空字符串,写作 String?:

var b: String? = "abc" //String? 表示该 String 类型变量可为空
b = null // 编译通过

看到这个?的时候,是不是发现和Groovy有点像?不过还是有一定区别的,这里就不展开了。

好了,书归正传,我们来看看作为一个TOIBE编程语言排行榜第一名的语言,Java语言对于NPE做出了哪些努力!

Java做了哪些努力

一直以来对于null和NPE的改进还是做出了一些努力的。

首先在Java 8中提供了Optional,其实在Java 8 推出之前,Google的Guava库中就率先提供过Optional接口来使null快速失败。

Optional在可能为null的对象上做了一层封装,Optional对象包含了一些方法来显式地处理某个值是存在还是缺失,Optional类强制你思考值不存在的情况,这样就能避免潜在的空指针异常。

但是设计Optional类的目的并不是完全取代null,它的目的是设计更易理解的API。通过Optional,可以从方法签名就知道这个函数有可能返回一个缺失的值,这样强制你处理这些缺失值的情况。

关于Optional的用法,不是本文的重点,就不在这里详细介绍了,笔者在日常开发中经常结合Stream一起使用Optional,还是比较好用的。

另外一个值得一提的就是最近(2020年03月17日)发布的JDK 14中对于NPE有了一个增强。那就是JEP 358: Helpful NullPointerExceptions

更有帮助的NPE

JDK 14中对于NEP有了一个增强,既然NPE暂时无法避免,那么就让他对开发者更有帮助一些。

每个Java开发人员都遇到过NullPointerExceptions (NPEs)。由于NPEs可以发生在程序的几乎任何地方,试图捕获并从它们中恢复通常是不切实际的。因此,开发人员通常依赖于JVM来确定NPE实际发生时的来源。例如,假设在这段代码中出现了一个NPE:

a.i = 99;

JVM将打印出导致NPE的方法、文件名和行号:

Exception in thread "main" java.lang.NullPointerException
at Prog.main(Prog.java:5)

通过以上堆栈信息,开发人员可以定位到a.i= 99这一行,并推断出a一定是null。

但是,对于更复杂的代码,如果不使用调试器,就不可能确定哪个变量是null。假设在这段代码中出现了一个NPE:

a.b.c.i = 99;

我们根本无法确定到底是a还是b或者是c在运行时是个null值。

但是,在JDK14以后,这种窘境就有解了。

在JDK14中,当运行期,试图对一个bull对象进行应用时,JVM依然会抛出一个NullPointerException (NPE),除此之外,还会通过通过分析程序的字节码指令,JVM将精确地确定哪个变量是null,并且在堆栈信息中明确的提示出来。

在JDK 14中,如果上文中的a.i = 99发生NPE,将会打印如下堆栈:

Exception in thread "main" java.lang.NullPointerException:
Cannot assign field "i" because "a" is null
at Prog.main(Prog.java:5)

如果是a.b.c.i = 99;中的b为null导致了空指针,则会打印以下堆栈信息:

Exception in thread "main" java.lang.NullPointerException:
Cannot read field "c" because "a.b" is null
at Prog.main(Prog.java:5)

可见,堆栈中明确指出了到底是哪个对象为null而导致了NPE,这样,一旦应用中发生NPE,开发者可以通过堆栈信息第一时间定位到到底是代码中的那个对象为null导致的。

这算是JDK的一个小小的改进,但是这个改进对于开发者来说确实是非常友好的。真的希望这些小而美的改动可以在JDK中越来越多。

参考资料:

https://openjdk.java.net/jeps/358

《Java 8 In Action》

Java 14 发布了,再也不怕 NullPointerException 了!的更多相关文章

  1. Java 14 发布了,可以扔掉Lombok了?

    2020年3月17日发布,Java正式发布了JDK 14 ,目前已经可以开放下载.在JDK 14中,共有16个新特性,本文主要来介绍其中的一个特性:JEP 359: Records 官方吐槽最为致命 ...

  2. JDK/Java 14 发布

    3 月 17 日,JDK/Java 14 正式 GA. 此版本包含的 JEP(Java/JDK Enhancement Proposals,JDK 增强提案)比 Java 12 和 13 加起来的还要 ...

  3. Java 8 到 Java 14,改变了哪些你写代码的方式?

    前几天,JDK 14 正式发布了,这次发布的新版本一共包含了16个新的特性. 其实,从Java8 到 Java14 ,真正的改变了程序员写代码的方式的特性并不多,我们这篇文章就来看一下都有哪些. La ...

  4. Java 14带来了许多新功能

    本文是作者翻译自java magazine的文章,我也将回持续的关注java的最新消息,即时和大家分享.如有翻译不准确的地方,欢迎大家留言,我将第一时间修改.   Java 14包含比前两个发行版更多 ...

  5. Java 14 新功能介绍

    不做标题党,认认真真写个文章. 文章已经收录在 Github.com/niumoo/JavaNotes 和未读代码博客,点关注,不迷路. Java 14 早在 2019 年 9 月就已经发布,虽然不是 ...

  6. Java 14 可能带来什么新特性?

    JDK/Java 13 在一个月前已经发布,该版本带来了 5 大新特性,笔者观察到其中的 Text Blocks(文本块)特性似乎被讨论最多. 文本块特性与常见的 Python "" ...

  7. Java 14 都要来了,为什么还有这么多人固守Java8?

    作者:刘欣 从Java 9开始,Java版本的发布就让人眼花缭乱了. 每隔6个月,都会冒出一个新版本出来,Java 10 , Java 11, Java 12, Java 13, 到2020年3月份, ...

  8. Java 14 令人期待的 5 大新特性,打包工具终于要来了

    随着新的 Java 发布生命周期的到来,新版本预计将于 2020 年 3 月发布,本文将对其中的 5 个主要特性作些概述. Java 13刚刚发布给开发人员使用不久,最新版本的JDK于2019年9月发 ...

  9. Java 14 有哪些新特性?

    记录为 Java 提供了一种正确实现数据类的能力,不再需要为实现数据类而编写冗长的代码.下面就来看看 Java 14 中的记录有哪些新特性. 作者 | Nathan Esquenazi 译者 | 弯月 ...

随机推荐

  1. Kubernetes详解

    1.1 Kubernetes简介 1.1.1 什么是Kubernetes Kubernetes (通常称为K8s,K8s是将8个字母“ubernete”替换为“8”的缩写) 是一个以容器为中心的基础架 ...

  2. less 的使用方法总结

    一. 安装和使用 LESS 1.1 安装 使用命令行安装 LESS npm install -g less 1.2 使用 less 有多种的使用方法,在这里我向大家介绍最常用的俩种方法. 第一种是直接 ...

  3. 吴裕雄--天生自然 Tensorflow卷积神经网络:花朵图片识别

    import os import numpy as np import matplotlib.pyplot as plt from PIL import Image, ImageChops from ...

  4. GDB调试指南-单步调试

    前言 前面通过<启动调试>,<断点设置>,<变量查看>,我们已经了解了GDB基本的启动,设置断点,查看变量等,如果这些内容你还不知道,建议先回顾一下前面的内容.在启 ...

  5. Oracle中的列转行实现字段拼接用例

    文章目录 Oracle中的列转行实现字段拼接 场景 在SQL使用过程中经常有这种需求:将某列字段拼接成in('XX','XX','XX','XX','XX','XX' ...)做为查询条件. 实现 s ...

  6. tp5.1 请求时间格式化

    当前时间:{$Request.time|date='Y-m-d H:i:s'} 注意database.php的配置!记录一下!

  7. 成长日记(2) Java面向对象

    本篇主要是记录自己在学习路上的笔记,如果有哪里记错了请大家直接指出 面向对象的概念 *人为抽象的一种编程模型 *面向过程 代码集中 难以维护 *类:对事物 算法 逻辑 概念等的抽象 理解成 模板 图纸 ...

  8. oa办公系统快速开发工具,助力企业优化升级

    随着互联网的快速发展.信息化 IT 技术的不断进步.移动互联新技术的兴起,不管是大的集团企业还是中小型企业,纸质化的办公模式已不能满足现有需求,构建oa平台,为员工提供高效的办公环境尤其重要. 我们先 ...

  9. Ansible-安装配置

    主机规划 主机名称 操作系统版本 内网IP 外网IP(模拟) 安装软件 ansi-manager CentOS7.5 172.16.1.180 10.0.0.180 ansible ansi-hapr ...

  10. 【原创】(求锤得锤的故事)Redis锁从面试连环炮聊到神仙打架。

    这是why技术的第38篇原创文章 又到了一周一次的分享时间啦,老规矩,还是先荒腔走板的聊聊生活. 有上面的图是读大学的时候,一次自行车骑行途中队友抓拍的我的照片.拍照的地方,名字叫做牛背山,一个名字很 ...