如何在 Java8 中風(fēng)騷走位避開空指針異常

要說 Java 編程中哪個(gè)異常是你印象最深刻的,那 NullPointerException 空指針可以說是臭名昭著的。不要說初級(jí)程序員會(huì)碰到, 即使是中級(jí),專家級(jí)程序員稍不留神,就會(huì)掉入這個(gè)坑里。

Null 引用的發(fā)明者 Tony Hoare 曾在 2009 年作出道歉聲明,聲明中表示,到目前為止,空指針異常大約給企業(yè)已造成數(shù)十億美元的損失。

下面是 Tony Hoare 的原話:

我將 Null 引用的設(shè)計(jì)稱為是一個(gè)數(shù)十億美元的錯(cuò)誤。1965 那年,我正在用面向?qū)ο笳Z言(ALGOL W) 設(shè)計(jì)首個(gè)功能全面的系統(tǒng)。當(dāng)時(shí)我的考量是,確保所有被使用的引用都是安全的,編譯器會(huì)自動(dòng)進(jìn)行檢查。但是,我沒有抵住誘惑,加入了 Null 引用,僅僅是為了實(shí)現(xiàn)起來省事。這之后,它導(dǎo)致了數(shù)不清的 bug、錯(cuò)誤和系統(tǒng)崩潰,也為企業(yè)導(dǎo)致了不可估量的損失。

事已至此,我們必須學(xué)會(huì)面對(duì)它。So, 我們要如何防止空指針異常呢?

唯一的辦法就是對(duì)可能為 Null 的對(duì)象添加檢查。但是 Null 檢查是繁瑣且痛苦的。所以一些比較新的語言為了處理 Null 檢查,特意添加了特殊的語法,如空合并運(yùn)算符。

在 Groovy 或 Kotlin 這樣的語言中也被稱為 Elvis 運(yùn)算符。

不幸的是,在老版本的 Java 中并沒有提供這樣的語法糖。Java8 中在這方面做了改進(jìn)。所以,這篇文章就特意來介紹一下如何在 Java8 中利用新特性來編寫防止 NullPointerException的發(fā)生。

Java8 中如何加強(qiáng)對(duì) Null 對(duì)象的檢查?

在業(yè)務(wù)系統(tǒng)中,對(duì)象中嵌套對(duì)象是經(jīng)常發(fā)生的場(chǎng)景,如下示例代碼:

// 最外層對(duì)象

class Outer {

Nested nested;

Nested getNested() {

return nested;

}

}

// 第二層對(duì)象

class Nested {

Inner inner;

Inner getInner() {

return inner;

}

}

// 最底層對(duì)象

class Inner {

String foo;

String getFoo() {

return foo;

}

}

業(yè)務(wù)中,假設(shè)我們需要獲取 Outer 對(duì)象對(duì)底層的 Inner 中的 foo 屬性,我們必須寫一堆的非空校驗(yàn),來防止發(fā)生 NullPointerException:

// 繁瑣的代碼

Outer outer = new Outer();

if (outer != null && outer.nested != null && outer.nested.inner != null) {

System.out.println(outer.nested.inner.foo);

}

通過 Optional

在 Java8 中,我們有更優(yōu)雅的解決方式,那就是使用 Optional是說,我們可以在一行代碼中,進(jìn)行流水式的 map 操作。而?map 方法內(nèi)部會(huì)自動(dòng)進(jìn)行空校驗(yàn)

Optional.of(new Outer())

.map(Outer::getNested)

.map(Nested::getInner)

.map(Inner::getFoo

.ifPresent(System.out::println); // 如果不為空,最終輸出 foo 的值

通過 suppiler 函數(shù)自定義增強(qiáng) API

上面這種方式個(gè)人感覺還是有點(diǎn)啰嗦,我們可以利用 suppiler 函數(shù)來出一個(gè)終極解決方案:

public static Optional resolve(Supplier resolver) {

try {

T result = resolver.get();

return Optional.ofNullable(result);

}

catch (NullPointerException e) {

// 可能會(huì)拋出空指針異常,直接返回一個(gè)空的 Optional 對(duì)象

return Optional.empty();

}

}

利用上面的 resolve 方法來重構(gòu)上述的非空校驗(yàn)代碼段:

Outer obj = new Outer();

// 直接調(diào)用 resolve 方法,內(nèi)部做空指針的處理

resolve(() -> obj.getNested().getInner().getFoo());

.ifPresent(System.out::println); // 如果不為空,最終輸出 foo 的值

總結(jié)

你需要知道的是,上面這兩個(gè)解決方案并沒傳統(tǒng)的 null 檢查性能那么高效。但在絕大部分業(yè)務(wù)場(chǎng)景下,舍棄那么一丟丟的性能來方便編碼,是完全可取, 除非是那種對(duì)性能有嚴(yán)格要求的場(chǎng)景,我們才不建議使用。

個(gè)人覺得,真要拿這點(diǎn)性能說事,還不如去優(yōu)化優(yōu)化 sql 語句,業(yè)務(wù)邏輯等。

最后送福利了,現(xiàn)在加群:810589193,點(diǎn)擊鏈接加入群聊【Java架構(gòu)學(xué)習(xí)交流群】:https://jq.qq.com/?_wv=1027&k=5deQUBl獲取Java工程化、高性能及分布式、高性能、高架構(gòu)、性能調(diào)優(yōu)、Spring、MyBatis、Netty源碼分析等多個(gè)知識(shí)點(diǎn)高級(jí)進(jìn)階干貨的直播免費(fèi)學(xué)習(xí)權(quán)限及相關(guān)視頻資料,還有spring和虛擬機(jī)等書籍掃描版

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本文是在學(xué)習(xí)和使用kotlin時(shí)的一些總結(jié)與體會(huì),一些代碼示例來自于網(wǎng)絡(luò)或Kotlin官方文檔,持續(xù)更新... 對(duì)...
    竹塵居士閱讀 3,479評(píng)論 0 8
  • 1、一個(gè)".java"源文件中是否可以包括多個(gè)類(不是內(nèi)部類)?有什么限制?答:可以有多個(gè)類,但只能有一個(gè)publ...
    岳小川閱讀 1,022評(píng)論 0 2
  • pyspark.sql模塊 模塊上下文 Spark SQL和DataFrames的重要類: pyspark.sql...
    mpro閱讀 9,914評(píng)論 0 13
  • 這個(gè)系列面試題主要目的是幫助你拿輕松到offer,同時(shí)還能開個(gè)好價(jià)錢。只要能夠搞明白這個(gè)系列的絕大多數(shù)題目,在面試...
    獨(dú)念白閱讀 405評(píng)論 0 3
  • 我們總會(huì)拿"神經(jīng)病",來開玩笑。所以我一直以為神經(jīng)病和精神病他們是一個(gè)意思,但是都過書之后算是第一次這么近距離的...
    RainbowKang閱讀 526評(píng)論 0 2

友情鏈接更多精彩內(nèi)容