摘要:我們應該考慮使用字符串常量調用方法來代替使用對象調用該方法。然而如果我們通過字符串常量來調用方法,執行流程會正常進行檢查方法的參數在執行方法的方法體之前,務必對方法的參數進行值檢查。
java.lang.NullPoinerException – 怎么處理空指針異常原文地址
作者 Sotirios-Efstathios (Stathis) Maneas
譯者 smallclover
Thanks for your watching!
在java中,null是一個特殊的值,它能夠被賦值給對象的引用。表示該對象的值不確定。當一個應用試圖使用或者訪問一個引用為null的對象時,一個NullPointerException 會被拋出。以下列舉的幾種情況將會拋出該異常:
通過一個 null 對象調用 方法
訪問或者修改一個null 對象的字段
獲取null的長度,比如,一個引用值為null的數組
訪問或修改一個null對象,比如 一個引用值為null的數組。
拋出 NullPointException
當你試圖同步一個null對象的時候。
NullPointerException是一個RuntimeException,并且javac編譯器將不會強制你使用try-catch代碼塊處理NullPointerException。
為什么我們需要null值?正如前面所提到的那樣,在java null值是一個特殊的值。Null值經常被使用在一些設計模式中,如Null Object Pattern 和 Singleton Pattern。前者,提供一個對象來代替當該類型的對象缺失的時候;后者確保該類有且僅有一個類的實例被創建。目的是為了提供一個該對象的全局唯一訪問點。
例如,生成一個類的唯一實例的一般方法是:聲明所有的構造函數為私有,然后創建一個公有的方法返回該類的唯一一個實例
TestSingleton.java:
01 import java.util.UUID; 02 03 class Singleton { 04 05 private static Singleton single = null; 06 private String ID = null; 07 08 private Singleton() { 09 /* Make it private, in order to prevent the creation of new instances of 10 * the Singleton class. */ 11 12 ID = UUID.randomUUID().toString(); // Create a random ID. 13 } 14 15 public static Singleton getInstance() { 16 if (single == null) 17 single = new Singleton(); 18 19 return single; 20 } 21 22 public String getID() { 23 return this.ID; 24 } 25 } 26 27 public class TestSingleton { 28 public static void main(String[] args) { 29 Singleton s = Singleton.getInstance(); 30 System.out.println(s.getID()); 31 } 32 }
在上面的這個例子中,我們聲明了一個Singleton類的靜態實例,該靜態實例在getInstance內部執行最多一次的初始化。注意,這里使用null值,使得唯一的實例被創建。
怎樣避免NullPointerException為了避免出現NullPointerException,在你使用該對象之前確保所有的對象被正常的初始化。注意,當你聲明了一個引用變量,你就真的創建了一個指向對象的指針。在你通過這個對象請求方法或者屬性時你必須確保這個指針不是null。
另外,如果該異常被拋出,請靈活的使用堆棧跟蹤中的信息,通過JVM提供的堆棧跟蹤,我們能夠調試應用。查找exception發生的方法以及位于該方法的多少行,然后判斷指定的那一行中哪一個引用等于null。在剩余的章節,我們將具體的描述一些處理上述異常情況的技術,然而它們也并不能完全消除NullPointerException的問題,所以程序員還是應該在編寫應用的時候仔細一些。
1. String與字符串常量作比較
在一個應用中經常會編寫涉及到比較String變量和字符串常量的代碼。這個字符串常量可能是String類型或者是Enum的一個元素。我們應該考慮使用字符串常量調用equals方法來代替使用null對象調用該方法。例如觀察一下案例:
1 String str = null; 2 if(str.equals("Test")) { 3 /* The code here will not be reached, as an exception will be thrown. */ 4 }
上面的代碼片段將會拋出NullPointerException。然而如果我們通過字符串常量來調用equals方法,執行流程會正常進行:
1 String str = null; 2 if("Test".equals(str)) { 3 /* Correct use case. No exception will be thrown. */ 4 }
2. 檢查方法的參數
在執行方法的方法體之前,務必對方法的參數進行null值檢查。只有在在確保屬性被檢查之后再繼續執行函數。另外,在傳遞的參數有錯誤的時候你可以拋出一個IllegalArgumentException通知調用者。
例如:
1 public static int getLength(String s) { 2 if (s == null) 3 throw new IllegalArgumentException("The argument cannot be null"); 4 5 return s.length(); 6 }
3. 使用String.valueOf()方法代替toString()
當你的應用代碼需要獲得一個對象的字符串的表示形式,請避免使用這個對象的toString方法,如果你的對象的引用等于null,將會導致NullPointerException被拋出。我們可以考慮使用靜態的String.valueOf方法,該方法不會拋出任何異常,當函數的參數為null時會打印一個“null”字符串。
譯注:這里只是一個建議,具體使用什么方法還是需要對應具體的生產環境
4. 使用三元運算符
三元運算符對于我們避開NullPointerException是非常有用的。該操作符格式如下:
1 boolean expression ? value1 : value2;
首先一個boolean表達式將會被判斷,如果表達式為true,value1的值將會被返回,否則,value2的值會被返回。我們使用三元表達式處理空指針,如下圖所示
1 String message = (str == null) ? "" : str.substring(0, 10);
在str的引用值為null的時候message變量的值為空,否則,如果str指向實際的數據,message將會獲取它前10個字符。
5. 創建一個返回空的集合的方法來代替null。
一個非常好的技巧就是創建一個能返回空集合的方法來代替null值。你的應用代碼能夠迭代這個空集合并且可以使用它的方法和屬性,而不用擔心拋出NullPointerException。例如
Example.java 01 public class Example { 02 private static Listnumbers = null; 03 04 public static List getList() { 05 if (numbers == null) 06 return Collections.emptyList(); 07 else 08 return numbers; 09 } 10 }
6. 使用Apache的StringUtils類
apache的Commons Lang類庫提供操作java.lang ApI的實用工具類,比如提供很多操作字符串的方法的類StringUtils.java,可以處理值為null的字符串。
你能使用諸如 StringUtils.isNotEmpty、StringUtils.IsEmpty和StringUtils.equals方法,為了減少NullPointerException。舉個例子:
1 if (StringUtils.isNotEmpty(str)) { 2 System.out.println(str.toString()); 3 }
7. 使用contains(),containsKey(),containsValue()方法
如果你的應用使用集合,例如Maps,那你應該考慮使用contains(),containsKey(),containsValue()方法,在你確定value存在于map的時候你可以根據指定的key來獲取值。
1 Mapmap = … 2 … 3 String key = … 4 String value = map.get(key); 5 System.out.println(value.toString()); // An exception will be thrown, if the value is null.
上面的代碼片段,我們沒有檢查是否這個key存在于map中,所以可能會返回null值,最安全的做法如下述代碼所示:
1 Mapmap = … 2 … 3 String key = … 4 if(map.containsKey(key)) { 5 String value = map.get(key); 6 System.out.println(value.toString()); // No exception will be thrown. 7 }
8. 檢查外部方法的返回值
使用第三方庫在我們的日常開發中是十分常見的。當這些libraries包含的方法返回引用的時候,確保方法返回的引用不是null。另外,應該認真的閱讀方法的Javadoc,為了更好的理解它的功能以及它的返回值。
9. 使用斷言
當你在測試代碼時使用斷言是十分有用的,為了避免執行的代碼片段拋出NullPointerException。Java斷言通過關鍵字assert來執行的并且在斷言出錯時會拋出AssertionError。
注意你必須明確的啟用斷言標志在JVM中,通過執行參數-ea.否則,斷言將會被完全忽略。
下面代碼是一個使用斷言的例子:
1 public static int getLength(String s) { 2 /* Ensure that the String is not null. */ 3 assert (s != null); 4 5 return s.length(); 6 }
如果你執行以上的代碼片段并且傳遞給getLength方法一個值為null的參數,然后會有如下的錯誤信息被打印出來:
1 Exception in thread "main" java.lang.AssertionError
10. 單元測試
當你在測試代碼的功能性和正確性的時候單元測試是極其有用的。當你的應用代碼出現特定的執行流程的時候,花一些時間寫一些測試案例來驗證該流程不會拋出NullPointerException。
Existing NullPointerException safe methods
1. 訪問一個類的靜態成員變量或者方法
當你的代碼試圖訪問一個類的靜態的變量和方法的時候,記事這個對象的引用為null,JVM也不會拋出NullPointerException。這是由于java編譯器會在特別的地方存儲靜態的變量和字段。因此,靜態字段和靜態方法并不會關聯到對象,而是與類的名字有關聯。
例如,下述代碼不會拋出NullPointerException。
TestStatic.java :
01 class SampleClass { 02 03 public static void printMessage() { 04 System.out.println("Hello from Java Code Geeks!"); 05 } 06 } 07 08 public class TestStatic { 09 public static void main(String[] args) { 10 SampleClass sc = null; 11 sc.printMessage(); 12 } 13 }
注意,盡管SampleClass對象的引用值為null,但是方法還是將被正確的執行。然而,當我們提到靜態方法和靜態字段時,最好的訪問方式仍然是靜態的訪問方式(通過類名來訪問),如SampleClass.printMessage().
2. Instanceof 操作
即使對象的引用值為null,instanceof操作也能被使用。Instanceof操作在引用的值為null的時候會返回false并且不會拋出NullPointerException。請思考一下代碼片段:
1 String str = null; 2 if(str instanceof String) 3 System.out.println("It"s an instance of the String class!"); 4 else 5 System.out.println("Not an instance of the String class!");
執行的結果不出所料:
1 Not an instance of the String class!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/66078.html
摘要:本文探討使用構建集成的可執行程序的方法,以及根節點問題。而使用后,可指導類作為根節點,避免了嵌套的情況。文件設計如下文件同時指明了根節點的類型,資源文件對應的設計如下此時可實現開始時,純代碼方式的自定義控件設計。 「博客搬家」 原地址: 簡書 原發表時間: 2017-05-22 上一篇文章探討了使用 IntelliJ IDEA 創建 JavaFX 工程,進而開發了所需應用程序。更...
摘要:關于異常處理的文章已有相當的篇幅,本文簡單總結了的異常處理機制,并結合代碼分析了一些異常處理的最佳實踐,對異常的性能開銷進行了簡單分析。是程序正常運行中,可以預料的意外情況,應該被捕獲并進行相應處理。 關于異常處理的文章已有相當的篇幅,本文簡單總結了Java的異常處理機制,并結合代碼分析了一些異常處理的最佳實踐,對異常的性能開銷進行了簡單分析。博客另一篇文章《[譯]Java異常處理的最...
摘要:最近在寫運維開發時經常碰見一些常見的文件的文件操作。將字符串寫入文件中讀取文件字符串文件不存在把文件轉為數組文件不存在從數據庫讀取數據存入文件數據庫操作將數組寫入文件調用時只需要把文件包含進來就可以 最近在寫運維開發時,經常碰見一些常見的文件的文件操作。特別在處理高并發的需求時,需要REDIS DOCUMENT DB同時操作,如果業務人員在文件處理上花費太多的時間會降低開發效率,因此筆...
摘要:指示該錯誤是否嚴重,此屬性會在該異常根據錯誤的上下文遍歷堆棧時進行更新,嚴重性會指示異常捕獲代碼是應該停止程序還是該繼續處理。引發異常在檢測到錯誤并無法從中恢復時,異常將向上傳播到調用堆棧,直到到達處理它的某個塊。 翻譯:瘋狂的技術宅 原文標題:Exception handling strategy 原文鏈接:http://programmergate.com/exc...本文首發微信...
摘要:的處理流程就是在樹中的傳遞的過程這個過程分為步第一步,在樹中尋找處理的第二步,剩余的在樹傳遞給目標第一步,在樹中尋找處理的遞歸方式完成從頂向下傳遞,找到到的最底層的從底向上,查找可以處理的并記錄從到的路徑其中涉及到以及遞歸的方式調用的尋找最 TouchEvent的處理流程就是TouchEvent在View樹中的傳遞的過程:這個過程分為2步:第一步,ACTION_DOWN在View樹中尋...
閱讀 3081·2021-11-24 10:47
閱讀 3831·2021-11-02 14:43
閱讀 2228·2021-09-26 10:15
閱讀 2254·2021-09-08 09:35
閱讀 560·2019-08-30 12:45
閱讀 2781·2019-08-29 17:04
閱讀 3214·2019-08-26 14:05
閱讀 1259·2019-08-26 12:10