java.lang.NullPointerException

之前一篇文章聊過異常排名《Java異常排行榜:哪個異常最常見?》,裡面談到國外一個網站對 Java 異常進行數據分析並排名,結果是 NullPointerException 排第一,本文正好對空指針異常做一個總結,希望對各位同學有所幫助。

在本文中,我展示了一個關於如何處理空指針異常的綜合示例。在Java中,null 作為一個特殊值被對象引用,用來表示該對象當前指向的是一塊未知內存數據。然而NullPointerException這個異常,則是程序在使用或訪問一個對象的引用時,而該對象等於null則被拋出。

那些情況會引發該異常呢?

  • 被調用方法的對象為null。
  • 訪問或修改一個null對象的字段。
  • 求一個數組為null對象的長度。
  • 訪問或修改一個數組為null對象中的某一個值。
  • 被拋出的值是null並且是一個Throwable的子類。
  • 當你用null對象進行synchronized代碼塊。

NullPointerException 是 RuntimeException 的子類,因此,Javac 編譯器並不會強迫你使用 try-catch 代碼塊來捕獲該異常。

一、為什麼需要 null ?

如上所述,null 是 Java 的一個特殊值。它在設計模式方面編碼的過程中非常有用,例如空對象模式和單例模式。空對象模式提供了一個對象作為缺少給定類型對象的代理。而單例模式可以確保只創建一個類的實例,主要用於提供一個全局訪問的對象。

java.lang.NullPointerException

二、如何避免空指針異常

另外,如果引發異常,請使用堆棧中的異常信息進行跟蹤。堆棧跟蹤是由JVM提供,便於應用程序的調試。找到發生異常的方法和代碼行,然後確定哪個引用為null。

在本文的餘下部分,我們將介紹一些避免空指針異常的方法。但是,這些方法並非一勞永逸,因此,同學們在編寫應用程序時應格外小心。

1、String變量與文本值比較

在編碼過程中,String變量與文本值之間的比較是特別常見的。一般被比較的值可以是一個字符串或枚舉值。因此,我們不要從空對象調用方法進行比較,而應考慮從文字值中調用方法。如下:

java.lang.NullPointerException

上面的代碼片段則會拋出一個NullPointerException。但是,如果我們從文字中調用方法,那麼執行流程通常會繼續:

java.lang.NullPointerException

為什麼呢?去看一下 JDK 源碼 java.lang.String 便明白了。

java.lang.NullPointerException

2、檢查方法的參數

在執行你自己的方法的主體之前,一定要檢查方法傳入的參數是否為空。只有在正確檢查了參數後,才能繼續執行該方法的相應邏輯。否則,您可以拋出一個 IllegalArgumentException 來通知調用方法所傳遞的參數有問題。

例如:

java.lang.NullPointerException

3、優先使用String.valueOf() 代替toString()

當您代碼中的某個對象需要用字符串的方式來表示時,請避免使用該對象的toString方法;因為若你的對象引用為null,則會拋出 NullPointerException。

相反,考慮使用靜態String.valueOf方法,該方法不會拋出任何異常,若對象引用為空,則打印「null」字符串。

有的同學可能會問為什麼呢?還是那句話讀源碼 ^_^

下面是基類 Object 的 toString() 方法:

java.lang.NullPointerException

接著,咱們再來看看 String 的 valueOf() 方法做了什麼呢?

java.lang.NullPointerException

4、使用三元運算符

該操作是非常有用的,可以幫助我們避免了NullPointerException。格式如下:

java.lang.NullPointerException

上面通過布爾表達式來判斷。如果表達式結果為true,則返回value1,否則返回value2。我們可以使用三元運算符來處理空指針,如下所示:

java.lang.NullPointerException

如果str的引用為空,則消息變量將為空。否則,如果str指向實際數據,則該消息將保留它的前10個字符。

這裡,一直有個疑惑困擾著我,為什麼 Kotlin 這門語言去掉了三元運算符呢?知道的同學歡迎留言,一起來探討~~~

5、創建返回空集合而不是null值的方法

一個非常好的操作是創建返回一個空集合的方法,而不是一個null值。因為你的代碼可以遍歷空集合並使用它的方法和字段,而不會拋出一個NullPointerException 。例如:

java.lang.NullPointerException

注意:要熟悉 Collections 這個集合工具類,裡面有太多好用的方法了。

6、使用Apache的StringUtils類

Apache的Commons Lang是一個為 java.lang API 提供幫助工具的庫,比如字符串操作方法。提供字符串操作的示例類是 StringUtils.java,它對輸入的字符串進行了 null 判斷。

你可以使用 StringUtils.isNotEmpty, StringUtils.IsEmpty 和 StringUtils.equals 等方法,來避免NullPointerException。例如:

java.lang.NullPointerException

還是老規矩,咱們來讀一下相應的源碼。

java.lang.NullPointerException

7、習慣用 contains(), containsKey(), containsValue() 方法

如果您的程序在使用集合,請考慮使用contains,containsKey和containsValue方法。例如,從集合中找一個特定鍵的值:

java.lang.NullPointerException

在上面的代碼片段中,我們未檢查key是否真的存在於內部Map,因此返回的值可以是 null 。最安全的方法如下:

java.lang.NullPointerException

8、請檢查使用的外部方法的返回值是否為 null

在編碼中使用外部庫是很常見的,這些庫可能包含返回引用的方法,需確保返回的值不為 null 。另外,我們要養成在開發的過程中,養成閱讀 Javadoc 的習慣,以便更好地理解其功能和返回值。

9、使用斷言

斷言在測試代碼時非常有用,並且可以被使用,以避免 NullPointerException 。Java 斷言是用 assert 關鍵字實現的,並拋出一個 AssertionError 。

請注意,您必須顯式啟用 JVM 的斷言標誌,一般在程序啟動時,使用 –ea 參數來啟用斷言。否則,斷言將被完全忽略。

使用 Java 斷言的示例如下:

java.lang.NullPointerException

如果您執行上面的代碼段並傳遞一個空參數getLength,則會出現以下錯誤消息:

java.lang.NullPointerException

最後,您可以使用測試框架 JUnit 提供的類 Assert 來使用斷言。

10、單元測試

在測試代碼的功能和正確性時,單元測試一般非常有用。因此,建議多花一些時間編寫一些測試用例,來避免程序出現 NullPointerException。目前,我司的代碼覆蓋率要達到 95% 以上才能通過。

三、擁有 NullPointerException 的安全方法

1、訪問類的靜態成員或方法

當你的代碼試圖訪問靜態變量或類的方法時,即使對象的引用等於 null,JVM 也不會拋出一個 NullPointerException 。這是由於Java編譯器在編譯過程中將靜態方法和字段存儲在方法區或者常量池。因此,靜態字段和方法不與對象相關聯,而與類的名稱相關聯。

例如,下面的代碼不會拋出NullPointerException:

java.lang.NullPointerException

注意,儘管 SampleClass 等於的實例 null 將會被正確執行。但是,對於靜態方法或字段,最好以靜態方式訪問它們,比如SampleClass.printMessage()。

2、instanceof 操作符

instanceof 即使對象的引用等於 null,也可以使用該運算符。在 instanceof 操作時,參考值等於為null,不拋出 NullPointerException,而是返回 false 。例如,下面的代碼片段:

java.lang.NullPointerException

正如預期的那樣,執行的結果是:

java.lang.NullPointerException

四、參考

https://github.com/apache/commons-lang

https://github.com/junit-team/junit4

https://examples.javacodegeeks.com/java-basics/exceptions/java-lang-nullpointerexception-how-to-handle-null-pointer-exception


分享到:


相關文章: