Java 14 发布了,再也不怕 NullPointerException 了!
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 了!的更多相关文章
- Java 14 发布了,可以扔掉Lombok了?
2020年3月17日发布,Java正式发布了JDK 14 ,目前已经可以开放下载.在JDK 14中,共有16个新特性,本文主要来介绍其中的一个特性:JEP 359: Records 官方吐槽最为致命 ...
- JDK/Java 14 发布
3 月 17 日,JDK/Java 14 正式 GA. 此版本包含的 JEP(Java/JDK Enhancement Proposals,JDK 增强提案)比 Java 12 和 13 加起来的还要 ...
- Java 8 到 Java 14,改变了哪些你写代码的方式?
前几天,JDK 14 正式发布了,这次发布的新版本一共包含了16个新的特性. 其实,从Java8 到 Java14 ,真正的改变了程序员写代码的方式的特性并不多,我们这篇文章就来看一下都有哪些. La ...
- Java 14带来了许多新功能
本文是作者翻译自java magazine的文章,我也将回持续的关注java的最新消息,即时和大家分享.如有翻译不准确的地方,欢迎大家留言,我将第一时间修改. Java 14包含比前两个发行版更多 ...
- Java 14 新功能介绍
不做标题党,认认真真写个文章. 文章已经收录在 Github.com/niumoo/JavaNotes 和未读代码博客,点关注,不迷路. Java 14 早在 2019 年 9 月就已经发布,虽然不是 ...
- Java 14 可能带来什么新特性?
JDK/Java 13 在一个月前已经发布,该版本带来了 5 大新特性,笔者观察到其中的 Text Blocks(文本块)特性似乎被讨论最多. 文本块特性与常见的 Python "" ...
- Java 14 都要来了,为什么还有这么多人固守Java8?
作者:刘欣 从Java 9开始,Java版本的发布就让人眼花缭乱了. 每隔6个月,都会冒出一个新版本出来,Java 10 , Java 11, Java 12, Java 13, 到2020年3月份, ...
- Java 14 令人期待的 5 大新特性,打包工具终于要来了
随着新的 Java 发布生命周期的到来,新版本预计将于 2020 年 3 月发布,本文将对其中的 5 个主要特性作些概述. Java 13刚刚发布给开发人员使用不久,最新版本的JDK于2019年9月发 ...
- Java 14 有哪些新特性?
记录为 Java 提供了一种正确实现数据类的能力,不再需要为实现数据类而编写冗长的代码.下面就来看看 Java 14 中的记录有哪些新特性. 作者 | Nathan Esquenazi 译者 | 弯月 ...
随机推荐
- Python包管理工具setuptools相关
setup函数常用参数: --name 包名称 --version 包版本 --author ...
- webpack进阶之loader篇
webpack的loaders是一大特色,也是很重要的一部分.这遍博客我将分类讲解一些常用的laoder 一.loaders之 预处理 css-loader 处理css中路径引用等问题 style-l ...
- 从ArrayList的优化中想到的
在JDK7中ArrayList有一个小的改动,使用延迟加载的思想,默认构造函数不再初始化生成一个大小为10的数组,而是将elementData先赋值为一个共享的空数组. package java.ut ...
- Linux sed命令实例解析
最近看project的makefile,又见到了sed的强大编辑能力,在makefile工作之前,通常都是执行脚本或者make menuconfig来配置好各种全局变量.sed活动阶段通常在bash ...
- 混乱中的ICO平台,会不会是下一个P2P的重灾区?
当众多巨头和创业者还在为共享打车.共享单车.VR.IP化.互联网金融沉迷时,一种全新的"众筹"正在造就一个又一个暴富神话.其名为ICO,即首次代币众筹,一般指区块链初创项目在众筹平 ...
- JQUERY的基本使用方法
#准备工作 在官网下载JQUERY库 将库在你需要的网页上引用 123 <head><script src="jquery.js"></script& ...
- Java入门教程十(抽象类接口内部类匿名类)
抽象类(abstract) 一个类只定义了一个为所有子类共享的一般形式,至于细节则交给每一个子类去实现,这种类没有任何具体的实例,只具有一些抽象的概念,那么这样的类称为抽象类. 在面向对象领域,抽象类 ...
- Java反射,泛型在Json中的运用
最近项目中遇到了Json数据自动获取的功能,不然令人想起java的反射,已经很长时间没复习java了正好一块连java的这一块内容一起过一遍.java中的反射无疑就相当于java开发者的春天,在众多的 ...
- YAML语法使用,JSR303数据校验
YAML YAML是 "YAML Ain't a Markup Language" (YAML不是一种置标语言)的递归缩写 # yaml配置 server: prot: YAML语 ...
- jsvascript篮球梦
首先让我们先欣赏一下效果图: html文本: <div class="box"> <img id="imgshow" src="la ...