自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

細說Java中的空指針異常

譯文
開發(fā) 前端
本文將和您討論空指針異常(Null Pointer Exception)的常見原因,以及如何避免它的各種優(yōu)秀實踐。

空指針(Null Pointer Exception,NPE)是Java中最常見不過的異常了。其原因雖然顯而易見,但是開發(fā)人員往往會忽略,或未能及時采取措施。本文將和您詳細討論空指針問題的根源,以及對應的解決方法。

空引用破壞了Java類型安全性

Java通過提供??編譯類型的安全性??(Compile Type Safety),來保證開發(fā)人員不會錯配不同的變量類型。在下面的示例中,我們試圖將整形(Integer)值分配給某個字符串(String)變量,而Java會及時提醒您。

Java雖然會在編譯過程中,去驗證變量和賦值的類型,但是由于空值(NULL)代表了所有未初始化的對象,因此空值可以被分配為任何類型(如下圖所示),且Java不會報錯。

例如,Java允許如下賦值情況的出現:

這些對象在未被初始化的情況下,就指向了空引用,往往會產生Java類型的安全性漏洞。如下代碼段所示,就Java而言,Null和真實對象可能并沒有什么區(qū)別,但是它會導致一些不可實現的操作:

同時,由于Null屬于String類型,因此在編譯如下代碼段時,Java甚至都不會有任何警告。

但是,我們一旦運行該程序代碼,就會出現失敗,并且會被提示如下的空指針異常:

空指針異常的定義

空指針異常屬于運行時的異常。當Java嘗試去調用真實對象上的任何方法時,如果在運行時中,該對象調用的是空引用(Null Reference),那么就會拋出異常。您可以通過鏈接--https://dzone.com/articles/java-exceptions-1,找到有關異常、及其根源的更多詳細信息。

由于種種原因,開發(fā)人員時常會忘記初始化對象和驗證對象。這往往是導致空指針異常的根源。下面讓我們根據上述例子,討論如何修復NPE。

解釋空指針異常

下面是一個帶有Address字段的User對象。它們都可能為空。

使用Simple != Null Check避免空指針異常

下面是通過簡單的檢查(并非Null Check),來防止該問題的發(fā)生:

作為改進方案,我們可以使用Optional,并通過map函數,編寫出如下類似于前例的等效語句:

與簡單的Null Check相比,Optional能夠再次確保我們在ifPresent lambda中使用的數據不為空。這里的再次是指:如果User或Address的確為空的話,而且ifPresent被忽略了的話,即使我們忘記了使用Optional的相關功能,它也會以突出顯示.get()的方式,并提醒我們?yōu)樵O計提供Null Check。

其實,早在2014年,Optional就作為可選特性,在Java 1.8中被發(fā)布了。不過由于如下原因,導致其至今未能被廣泛地使用:

  • 由于Java本身非常冗長,因此Optional也跟著變得冗長起來,因此容易影響到代碼的整體質量。
  • 對于各種map/flatmap/ifpresent邏輯,開發(fā)人員更傾向使用簡單明了的Null Check。
  • Optional本身可能會導致開發(fā)人員創(chuàng)建更多NPE,例如使用到Optional.of(nullable)。

因此,鑒于上述原因,一些開發(fā)團隊會更喜歡使用Null Check,并且會用一堆邏輯性的測試覆蓋率,來避免潛在的NPE。

Null Check和Optional真的能夠解決問題嗎?

雖然上面討論到的Null Check與Optional的使用目的都是針對空值數據進行驗證。其中,Optional還可以提醒開發(fā)者返回值為空。但是,它們無法解決隱藏在開發(fā)者頭腦中的關鍵問題——在編譯步驟中出現的疏忽與遺漏。

@NotNull@Nullable注釋處理器有助于識別潛在的空值

因此,我們需要一個解決方案,可以在編譯步驟中讀取代碼,并通知開發(fā)人員他們可能疏漏的潛在NPE場景。對此,我們可以使用具有豐富功能的Java注釋處理器(Annotation Processors)。您可以通過鏈接--https://www.javacodegeeks.com/2015/09/java-annotation-processors.html,了解如何使用注釋處理器,來檢查可變性的示例。

目前,業(yè)界有幾種與NPE問題相關的注釋處理器。它們并非遵循完全相同的方法。下面我們將重點討論@NotNull和@Nullable兩種注釋提供工具。

Lombok的@NotNull注釋

Lombok(譯者注:一種Java庫,提供了一組非常實用的注釋)的@NotNull注釋可用于生成那些僅在運行時(Runtime)阻斷執(zhí)行的非Null Check。下面的代碼段展示了該注釋、及其等效語句。

檢查器框架的@NonNull和@Nullable注釋處理器

??檢查器框架??(Checker Framework)提供了@NonNull和@Nullable注釋,以及可以識別潛在Null Check的編譯處理器的步驟。該框架可以通過強制開發(fā)人員指定的Nullability,來發(fā)現潛在的空值。因此,您的代碼必須明確聲明可返回的結果為Nullable或NotNullable。下面讓我們來看一個可能返回Null,而非String的簡單方法:

現在,讓我們使用檢查器框架,來檢驗是否可以完成編譯。

如您所見,它報出了錯誤,并且返回了一個未使用@Nullable注釋標記的疑似空字符串。那么,讓我們將其標記為@Nullable試試:

如果我們再次運行編譯檢查,則會得到如下錯誤信息:

可見,檢查器框架在第19行發(fā)現了一個潛在問題,即:我們在Nullable字符串上調用了.length()。下面,讓我們使用Null Check和Optional的ifPresent來予以修復:

在編譯之后,我們將得到如下成功的構建信息:

檢查器框架的限制

至此,檢查器框架向我們展示了良好的檢查結果,并且突出了潛在的NPE。不過,其代價是我們必須通過@Nullable方法,標記所有可能為空的方法。為了突破該強制性的限制,我們可以創(chuàng)建一個帶有兩個字段的簡單類,并且將其中一個字段標記為@NonNull:

下面是經由檢查器框架的檢查結果:

顯然,檢查器框架會強制要求我們構造一個初始化id值的構造函數,例如:

可見,檢查器框架不僅能夠識別潛在的NPE,而且還會迫使我們遵循特定的設計要求。這在某種程度上犧牲了框架開發(fā)的靈活性。如果您對該問題有興趣的話,可以通過如下命令克隆我為您準備的示例:

git clone https://github.com/isicju/checker_framework_example


若要運行檢查器框架的話,請使用如下命令:

Mvn clean compile


檢查器框架的替代方案:Intellij Idea @NotNull注釋

當然,檢查器框架并非唯一的解決方案,Intellij Idea也提供了自己的注釋--@NotNull和@Nullable,并嵌入到了IDE的插件中。目前,我尚未找到在maven編譯步驟中添加它的方法。如果您對此有經驗的話,歡迎您追加評論。

小結

通過上述討論,我們可以看到,避免空指針異常的方法可以總結為:

  • 首選使用Optional,而不是傳遞Null
  • 使用檢查器框架

當然,檢查器框架會給您的開發(fā)帶來一些限制。在實踐中,如果您必須避免使用Lombok、甚至是Builder Pattern(建造者模式)的話,我建議您基于生產環(huán)境的穩(wěn)定性考慮,去使用檢查器框架。

譯者介紹

陳 峻 (Julian Chen),51CTO社區(qū)編輯,具有十多年的IT項目實施經驗,善于對內外部資源與風險實施管控,專注傳播網絡與信息安全知識與經驗;持續(xù)以博文、專題和譯文等形式,分享前沿技術與新知;經常以線上、線下等方式,開展信息安全類培訓與授課。

【原標題】Null Pointer Exception in Java: Causes and Ways to Avoid It(作者:Dmitry Egorov)


責任編輯:華軒 來源: 51CTO
相關推薦

2015-07-30 10:12:32

JavaNullAssert

2023-10-08 11:09:22

Optional空指針

2024-02-01 12:09:17

Optional容器null

2024-02-28 09:03:20

Optional空指針Java

2021-02-28 21:47:51

Java語法糖算數

2021-05-12 07:03:25

Switch報空指針

2015-01-05 10:01:20

Java

2021-11-15 06:56:45

系統(tǒng)運行空指針

2024-12-06 10:12:20

2024-08-12 08:28:35

2020-11-02 07:43:33

前端空指針數據

2022-01-04 23:13:57

語言PanicGolang

2009-09-28 11:25:17

PersistentQKestrelScala

2009-06-25 14:05:40

Java應用技巧

2020-07-02 22:42:18

Java異常編程

2024-10-15 15:58:11

2013-04-07 10:01:26

Java異常處理

2015-03-16 10:33:14

Swift指針

2011-04-11 11:09:50

this指針

2015-01-21 16:25:29

Swift指針
點贊
收藏

51CTO技術棧公眾號