避免 Java 應用中 NullPointerException 的技巧和最佳實踐

Java應用中拋出空指針異常是解決空指針的最好方式,也是寫出能順利工做的健壯程序的關鍵。熟話說「預防勝於治療」,對於這麼使人討厭的空指針異常,這句話也是成立的。值得慶幸的是運用一些防護性的編碼技巧,跟蹤應用中多個部分之間的聯繫,你能夠將Java中的空指針異常控制在一個很好的水平上。順便說一句,這是Javarevisited上的第二個空指針異常的帖子。在上個帖子中咱們討論了Java中致使空指針異常的常見緣由,而在本教程中咱們將會學習一些Java的編程技巧和最佳實踐。這些技巧能夠幫助你避免Java中的空指針異常。聽從這些技巧一樣能夠減小Java代碼中處處都有的非空檢查的數量。做爲一個有經驗的Java程序員,你可能已經知道其中的一部分技巧而且應用在你的項目中。但對於新手和中級開發人員來講,這將是很值得學習的。順便說一句,若是你知道其它的避免空指針異常和減小空指針檢查的Java技巧,請和咱們分享。 html

這些都是簡單的技巧,很容易應用,可是對代碼質量和健壯性有顯著影響。根據個人經驗,只有第一個技巧能夠顯著改善代碼質量。如我以前所講,若是你知道任何避免空指針異常和減小空指針檢查的Java技巧,你能夠經過評論本文來和分享。 java

1) 從已知的String對象中調用equals()和equalsIgnoreCase()方法,而非未知對象。

老是從已知的非空String對象中調用equals()方法。由於equals()方法是對稱的,調用a.equals(b)和調用b.equals(a)是徹底相同的,這也是爲何程序員對於對象a和b這麼不上心。若是調用者是空指針,這種調用可能致使一個空指針異常
Object unknownObject = null;
 
//錯誤方式 – 可能致使 NullPointerException
if(unknownObject.equals("knownObject")){
   System.err.println("This may result in NullPointerException if unknownObject is null");
}
 
//正確方式 - 即使 unknownObject是null也能避免NullPointerException
if("knownObject".equals(unknownObject)){
    System.err.println("better coding avoided NullPointerException");
}
這是避免空指針異常最簡單的Java技巧,但可以致使巨大的改進,由於equals()是一個常見方法。

2) 當valueOf()和toString()返回相同的結果時,寧願使用前者。

由於調用null對象的toString()會拋出空指針異常,若是咱們可以使用valueOf()得到相同的值,那寧願使用valueOf(),傳遞一個null給valueOf()將會返回「null」,尤爲是在那些包裝類,像Integer、Float、Double和BigDecimal。
BigDecimal bd = getPrice();
System.out.println(String.valueOf(bd)); //不會 NPE,但要注意和 String.valueOf(null) 的區別
System.out.println(bd.toString()); //拋出 "Exception in thread "main" java.lang.NullPointerException"

3) 使用null安全的方法和庫 

有不少開源庫已經爲您作了繁重的空指針檢查工做。其中最經常使用的一個的是Apache commons 中的StringUtils。你可使用StringUtils.isBlank(),isNumeric(),isWhiteSpace()以及其餘的工具方法而不用擔憂空指針異常。 程序員

//StringUtils方法是空指針安全的,他們不會拋出空指針異常
System.out.println(StringUtils.isEmpty(null));
System.out.println(StringUtils.isBlank(null));
System.out.println(StringUtils.isNumeric(null));
System.out.println(StringUtils.isAllUpperCase(null));
 
Output:
true
true
false
false
可是在作出結論以前,不要忘記閱讀空指針方法的類的文檔。這是另外一個不須要下大功夫就能獲得很大改進的Java最佳實踐。

(PS:也能夠考慮 google 的 Guava 庫:com.google.common.base.Preconditions.checkNotNull
com.google.common.base.Optional)
數據庫

4) 避免從方法中返回空指針,而是返回空collection或者空數組。

這個Java最佳實踐或技巧由Joshua Bloch在他的書Effective Java中提到。這是另一個能夠更好的使用Java編程的技巧。 經過返回一個空collection或者空數組,你能夠確保在調用如size(),length()的時候不會由於空指針異常崩潰。 Collections類提供了方便的空List,Set和Map: Collections.EMPTY_LIST,Collections.EMPTY_SET,Collections.EMPTY_MAP。這裏是實例。
public List getOrders(Customer customer){
    List result = Collections.EMPTY_LIST;
    return result;
}
你一樣可使用Collections.EMPTY_SET和Collections.EMPTY_MAP來代替空指針。

5) 使用annotation@NotNull 和 @Nullable

在寫程序的時候你能夠定義是否可爲空指針。經過使用像@NotNull和@Nullable之類的annotation來聲明一個方法是不是空指針安全的。現代的編譯器、IDE或者工具能夠讀此annotation並幫你添加忘記的空指針檢查,或者向你提示出沒必要要的亂七八糟的空指針檢查。IntelliJ和findbugs已經支持了這些annotation。這些annotation一樣是JSR 305的一部分,但即使IDE或工具中沒有,這個annotation自己能夠做爲文檔。看到@NotNull和@Nullable,程序員本身能夠決定是否作空指針檢查。順便說一句,這個技巧對Java程序員來講相對比較新,要採用須要一段時間。

6) 避免你的代碼中沒必要要的自動裝箱(autoboxing)和拆箱(unboxing)。

且無論其餘如建立臨時對象的缺點,若是wrapper類對象是null,自動裝箱一樣容易致使空指針異常。例如若是person對象沒有電話號碼的話會返回null,以下代碼會由於空指針異常崩潰。
Person ram = new Person("ram"); 
int phone = ram.getPhone(); // if ram.getPhone() == null, here will thown NPE.
當使用自動裝箱和自動拆箱的時候,不只僅是等號,< 或者 > 一樣會拋出空指針異常。你能夠經過這篇文章來學習更多的Java中的自動裝箱和拆箱的陷阱

(PS:因此儘可能避免返回包裝類型,返回原始類型就能避免此類 NPE 問題了,或者返回類型與接收類型一致)
編程

7) 聽從Contract並定義合理的默認值。

在Java中避免空指針異常的一個最好的方法是簡單的定義contract並聽從它們。大部分空指針異常的出現是由於使用不完整的信息建立對象或者未提供全部的依賴項。若是你不容許建立不完整的對象並優雅地拒絕這些請求,你能夠在接下來的工做者預防大量的空指針異常。相似的,若是對象容許建立,你須要給他們定義一個合理的默認值。例如一個Employee對象不能在建立的時候沒有id和name,可是是否有電話號碼是可選的。如今若是Employee沒有電話號碼,你能夠返回一個默認值(例如0)來代替返回null。可是必須謹慎選擇,喲有時候檢查空指針比調用無效號碼要方便。一樣的,經過定義什麼能夠是null,什麼不能爲null,調用者能夠做出明智的決定。failing fast 或接受 null 一樣是一個你須要進行選擇並貫徹的,重要的設計決策。

8)定義數據庫中的字段是否可爲空。

若是你在使用數據庫來保存你的域名對象,如Customers,Orders 等,你須要在數據庫自己定義是否爲空的約束。由於數據庫會從不少代碼中獲取數據,數據庫中有是否爲空的檢查能夠確保你的數據健全。在數據空中維護null約束一樣能夠幫助你減小Java代碼中的空指針檢查。當從數據庫中加載一個對象是你會明確,哪些字段是能夠爲null的,而哪些不能,這可使你代碼中沒必要要的!= null檢查最少化。

9) 使用空對象模式(Null Object Pattern)

還有一種方法來避免Java中的空指針異常。若是一個方法返回對象,在調用者中執行一些操做,例如Collection.iterator()方法返回迭代器,其調用者執行遍歷。假設若是一個調用者並無任何迭代器,其能夠返回空對象(Null object)而非null。空對象是一個特殊的對象,其在不一樣的上下文中有不一樣的意義。例如一個空的迭代器調用hasNext()返回false時,能夠是一個空對象。一樣的在返回Container和Collection類型方法的例子中,空對象能夠被用來代替null做爲返回值。我打算另寫一篇文章來說空對象模式,分享幾個Java空對象的例子。 數組

以上就是所有內容了,這是幾個易於聽從的避免空指針異常的Java技巧和最佳實踐。你應該能體會到這些技巧將很是有用,且不太難實現。若是你有其餘處理/預防空指針異常的技巧,而又沒包含在這裏,請經過評論來和咱們分享,我將收錄在這裏。

10) Refer:

http://javarevisited.blogspot.com/2013/05/ava-tips-and-best-practices-to-avoid-nullpointerexception-program-application.html 安全

相關文章
相關標籤/搜索