(點選上方公眾號,可快速關註)
來源:ImportNew – 熊鐸
Java應用中丟擲的空指標異常是解決空指標的最好方式,也是寫出能順利工作的健壯程式的關鍵。俗話說“預防勝於治療”,對於這麼令人討厭的空指標異常,這句話也是成立的。值得慶幸的是運用一些防禦性的編碼技巧,跟蹤應用中多個部分之間的聯絡,你可以將Java中的空指標異常控制在一個很好的水平上。順便說一句,這是Javarevisited上的第二個空指標異常的帖子。在上個帖子中我們討論了Java中導致空指標異常的常見原因,而在本教程中我們將會學習一些Java的程式設計技巧和最佳實踐。這些技巧可以幫助你避免Java中的空指標異常。遵從這些技巧同樣可以減少Java程式碼中到處都有的非空檢查的數量。作為一個有經驗的Java程式員,你可能已經知道其中的一部分技巧並且應用在你的專案中。但對於新手和中級開發人員來說,這將是很值得學習的。順便說一句,如果你知道其它的避免空指標異常和減少空指標檢查的Java技巧,請和我們分享。
http://javarevisited.blogspot.com/2012/06/common-cause-of-javalangnullpointerexce.html
這些都是簡單的技巧,很容易應用,但是對程式碼質量和健壯性有顯著影響。根據我的經驗,只有第一個技巧可以顯著改善程式碼質量。如我之前所講,如果你知道任何避免空指標異常和減少空指標檢查的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)); //不會丟擲空指標異常
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最佳實踐。
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) 避免你的程式碼中不必要的自動包裝和自動解包。
且不管其他如建立臨時物件的缺點,如果wrapper類物件是null,自動包裝同樣容易導致空指標異常。例如如果person物件沒有電話號碼的話會傳回null,如下程式碼會因為空指標異常崩潰。
Person ram = new Person(“ram”);
int phone = ram.getPhone();
當使用自動包裝和自動解包的時候,不僅僅是等號,< > 同樣會丟擲空指標異常。你可以透過這篇文章來學習更多的Java中的自動包裝和拆包的陷阱。
http://javarevisited.blogspot.com/2012/07/auto-boxing-and-unboxing-in-java-be.html
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技巧和最佳實踐。你可以欣賞到這些技巧將非常有用,且不太難實現。如果你有其他比秒這個異常的技巧,而又沒包含在這裡,請透過評論來和我們分享,我將收錄在這裡。
看完本文有收穫?請轉發分享給更多人
關註「ImportNew」,提升Java技能