摘要:我們應(yīng)該考慮使用字符串常量調(diào)用方法來代替使用對(duì)象調(diào)用該方法。然而如果我們通過字符串常量來調(diào)用方法,執(zhí)行流程會(huì)正常進(jìn)行檢查方法的參數(shù)在執(zhí)行方法的方法體之前,務(wù)必對(duì)方法的參數(shù)進(jìn)行值檢查。
java.lang.NullPoinerException – 怎么處理空指針異常原文地址
作者 Sotirios-Efstathios (Stathis) Maneas
譯者 smallclover
Thanks for your watching!
在java中,null是一個(gè)特殊的值,它能夠被賦值給對(duì)象的引用。表示該對(duì)象的值不確定。當(dāng)一個(gè)應(yīng)用試圖使用或者訪問一個(gè)引用為null的對(duì)象時(shí),一個(gè)NullPointerException 會(huì)被拋出。以下列舉的幾種情況將會(huì)拋出該異常:
通過一個(gè) null 對(duì)象調(diào)用 方法
訪問或者修改一個(gè)null 對(duì)象的字段
獲取null的長度,比如,一個(gè)引用值為null的數(shù)組
訪問或修改一個(gè)null對(duì)象,比如 一個(gè)引用值為null的數(shù)組。
拋出 NullPointException
當(dāng)你試圖同步一個(gè)null對(duì)象的時(shí)候。
NullPointerException是一個(gè)RuntimeException,并且javac編譯器將不會(huì)強(qiáng)制你使用try-catch代碼塊處理NullPointerException。
為什么我們需要null值?正如前面所提到的那樣,在java null值是一個(gè)特殊的值。Null值經(jīng)常被使用在一些設(shè)計(jì)模式中,如Null Object Pattern 和 Singleton Pattern。前者,提供一個(gè)對(duì)象來代替當(dāng)該類型的對(duì)象缺失的時(shí)候;后者確保該類有且僅有一個(gè)類的實(shí)例被創(chuàng)建。目的是為了提供一個(gè)該對(duì)象的全局唯一訪問點(diǎn)。
例如,生成一個(gè)類的唯一實(shí)例的一般方法是:聲明所有的構(gòu)造函數(shù)為私有,然后創(chuàng)建一個(gè)公有的方法返回該類的唯一一個(gè)實(shí)例
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 }
在上面的這個(gè)例子中,我們聲明了一個(gè)Singleton類的靜態(tài)實(shí)例,該靜態(tài)實(shí)例在getInstance內(nèi)部執(zhí)行最多一次的初始化。注意,這里使用null值,使得唯一的實(shí)例被創(chuàng)建。
怎樣避免NullPointerException為了避免出現(xiàn)NullPointerException,在你使用該對(duì)象之前確保所有的對(duì)象被正常的初始化。注意,當(dāng)你聲明了一個(gè)引用變量,你就真的創(chuàng)建了一個(gè)指向?qū)ο蟮闹羔?。在你通過這個(gè)對(duì)象請(qǐng)求方法或者屬性時(shí)你必須確保這個(gè)指針不是null。
另外,如果該異常被拋出,請(qǐng)靈活的使用堆棧跟蹤中的信息,通過JVM提供的堆棧跟蹤,我們能夠調(diào)試應(yīng)用。查找exception發(fā)生的方法以及位于該方法的多少行,然后判斷指定的那一行中哪一個(gè)引用等于null。在剩余的章節(jié),我們將具體的描述一些處理上述異常情況的技術(shù),然而它們也并不能完全消除NullPointerException的問題,所以程序員還是應(yīng)該在編寫應(yīng)用的時(shí)候仔細(xì)一些。
1. String與字符串常量作比較
在一個(gè)應(yīng)用中經(jīng)常會(huì)編寫涉及到比較String變量和字符串常量的代碼。這個(gè)字符串常量可能是String類型或者是Enum的一個(gè)元素。我們應(yīng)該考慮使用字符串常量調(diào)用equals方法來代替使用null對(duì)象調(diào)用該方法。例如觀察一下案例:
1 String str = null; 2 if(str.equals("Test")) { 3 /* The code here will not be reached, as an exception will be thrown. */ 4 }
上面的代碼片段將會(huì)拋出NullPointerException。然而如果我們通過字符串常量來調(diào)用equals方法,執(zhí)行流程會(huì)正常進(jìn)行:
1 String str = null; 2 if("Test".equals(str)) { 3 /* Correct use case. No exception will be thrown. */ 4 }
2. 檢查方法的參數(shù)
在執(zhí)行方法的方法體之前,務(wù)必對(duì)方法的參數(shù)進(jìn)行null值檢查。只有在在確保屬性被檢查之后再繼續(xù)執(zhí)行函數(shù)。另外,在傳遞的參數(shù)有錯(cuò)誤的時(shí)候你可以拋出一個(gè)IllegalArgumentException通知調(diào)用者。
例如:
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()
當(dāng)你的應(yīng)用代碼需要獲得一個(gè)對(duì)象的字符串的表示形式,請(qǐng)避免使用這個(gè)對(duì)象的toString方法,如果你的對(duì)象的引用等于null,將會(huì)導(dǎo)致NullPointerException被拋出。我們可以考慮使用靜態(tài)的String.valueOf方法,該方法不會(huì)拋出任何異常,當(dāng)函數(shù)的參數(shù)為null時(shí)會(huì)打印一個(gè)“null”字符串。
譯注:這里只是一個(gè)建議,具體使用什么方法還是需要對(duì)應(yīng)具體的生產(chǎn)環(huán)境
4. 使用三元運(yùn)算符
三元運(yùn)算符對(duì)于我們避開NullPointerException是非常有用的。該操作符格式如下:
1 boolean expression ? value1 : value2;
首先一個(gè)boolean表達(dá)式將會(huì)被判斷,如果表達(dá)式為true,value1的值將會(huì)被返回,否則,value2的值會(huì)被返回。我們使用三元表達(dá)式處理空指針,如下圖所示
1 String message = (str == null) ? "" : str.substring(0, 10);
在str的引用值為null的時(shí)候message變量的值為空,否則,如果str指向?qū)嶋H的數(shù)據(jù),message將會(huì)獲取它前10個(gè)字符。
5. 創(chuàng)建一個(gè)返回空的集合的方法來代替null。
一個(gè)非常好的技巧就是創(chuàng)建一個(gè)能返回空集合的方法來代替null值。你的應(yīng)用代碼能夠迭代這個(gè)空集合并且可以使用它的方法和屬性,而不用擔(dān)心拋出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的實(shí)用工具類,比如提供很多操作字符串的方法的類StringUtils.java,可以處理值為null的字符串。
你能使用諸如 StringUtils.isNotEmpty、StringUtils.IsEmpty和StringUtils.equals方法,為了減少NullPointerException。舉個(gè)例子:
1 if (StringUtils.isNotEmpty(str)) { 2 System.out.println(str.toString()); 3 }
7. 使用contains(),containsKey(),containsValue()方法
如果你的應(yīng)用使用集合,例如Maps,那你應(yīng)該考慮使用contains(),containsKey(),containsValue()方法,在你確定value存在于map的時(shí)候你可以根據(jù)指定的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.
上面的代碼片段,我們沒有檢查是否這個(gè)key存在于map中,所以可能會(huì)返回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. 檢查外部方法的返回值
使用第三方庫在我們的日常開發(fā)中是十分常見的。當(dāng)這些libraries包含的方法返回引用的時(shí)候,確保方法返回的引用不是null。另外,應(yīng)該認(rèn)真的閱讀方法的Javadoc,為了更好的理解它的功能以及它的返回值。
9. 使用斷言
當(dāng)你在測(cè)試代碼時(shí)使用斷言是十分有用的,為了避免執(zhí)行的代碼片段拋出NullPointerException。Java斷言通過關(guān)鍵字assert來執(zhí)行的并且在斷言出錯(cuò)時(shí)會(huì)拋出AssertionError。
注意你必須明確的啟用斷言標(biāo)志在JVM中,通過執(zhí)行參數(shù)-ea.否則,斷言將會(huì)被完全忽略。
下面代碼是一個(gè)使用斷言的例子:
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 }
如果你執(zhí)行以上的代碼片段并且傳遞給getLength方法一個(gè)值為null的參數(shù),然后會(huì)有如下的錯(cuò)誤信息被打印出來:
1 Exception in thread "main" java.lang.AssertionError
10. 單元測(cè)試
當(dāng)你在測(cè)試代碼的功能性和正確性的時(shí)候單元測(cè)試是極其有用的。當(dāng)你的應(yīng)用代碼出現(xiàn)特定的執(zhí)行流程的時(shí)候,花一些時(shí)間寫一些測(cè)試案例來驗(yàn)證該流程不會(huì)拋出NullPointerException。
Existing NullPointerException safe methods
1. 訪問一個(gè)類的靜態(tài)成員變量或者方法
當(dāng)你的代碼試圖訪問一個(gè)類的靜態(tài)的變量和方法的時(shí)候,記事這個(gè)對(duì)象的引用為null,JVM也不會(huì)拋出NullPointerException。這是由于java編譯器會(huì)在特別的地方存儲(chǔ)靜態(tài)的變量和字段。因此,靜態(tài)字段和靜態(tài)方法并不會(huì)關(guān)聯(lián)到對(duì)象,而是與類的名字有關(guān)聯(lián)。
例如,下述代碼不會(huì)拋出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對(duì)象的引用值為null,但是方法還是將被正確的執(zhí)行。然而,當(dāng)我們提到靜態(tài)方法和靜態(tài)字段時(shí),最好的訪問方式仍然是靜態(tài)的訪問方式(通過類名來訪問),如SampleClass.printMessage().
2. Instanceof 操作
即使對(duì)象的引用值為null,instanceof操作也能被使用。Instanceof操作在引用的值為null的時(shí)候會(huì)返回false并且不會(huì)拋出NullPointerException。請(qǐng)思考一下代碼片段:
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!");
執(zhí)行的結(jié)果不出所料:
1 Not an instance of the String class!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/66078.html
摘要:本文探討使用構(gòu)建集成的可執(zhí)行程序的方法,以及根節(jié)點(diǎn)問題。而使用后,可指導(dǎo)類作為根節(jié)點(diǎn),避免了嵌套的情況。文件設(shè)計(jì)如下文件同時(shí)指明了根節(jié)點(diǎn)的類型,資源文件對(duì)應(yīng)的設(shè)計(jì)如下此時(shí)可實(shí)現(xiàn)開始時(shí),純代碼方式的自定義控件設(shè)計(jì)。 「博客搬家」 原地址: 簡(jiǎn)書 原發(fā)表時(shí)間: 2017-05-22 上一篇文章探討了使用 IntelliJ IDEA 創(chuàng)建 JavaFX 工程,進(jìn)而開發(fā)了所需應(yīng)用程序。更...
摘要:關(guān)于異常處理的文章已有相當(dāng)?shù)钠疚暮?jiǎn)單總結(jié)了的異常處理機(jī)制,并結(jié)合代碼分析了一些異常處理的最佳實(shí)踐,對(duì)異常的性能開銷進(jìn)行了簡(jiǎn)單分析。是程序正常運(yùn)行中,可以預(yù)料的意外情況,應(yīng)該被捕獲并進(jìn)行相應(yīng)處理。 關(guān)于異常處理的文章已有相當(dāng)?shù)钠?,本文?jiǎn)單總結(jié)了Java的異常處理機(jī)制,并結(jié)合代碼分析了一些異常處理的最佳實(shí)踐,對(duì)異常的性能開銷進(jìn)行了簡(jiǎn)單分析。博客另一篇文章《[譯]Java異常處理的最...
摘要:最近在寫運(yùn)維開發(fā)時(shí)經(jīng)常碰見一些常見的文件的文件操作。將字符串寫入文件中讀取文件字符串文件不存在把文件轉(zhuǎn)為數(shù)組文件不存在從數(shù)據(jù)庫讀取數(shù)據(jù)存入文件數(shù)據(jù)庫操作將數(shù)組寫入文件調(diào)用時(shí)只需要把文件包含進(jìn)來就可以 最近在寫運(yùn)維開發(fā)時(shí),經(jīng)常碰見一些常見的文件的文件操作。特別在處理高并發(fā)的需求時(shí),需要REDIS DOCUMENT DB同時(shí)操作,如果業(yè)務(wù)人員在文件處理上花費(fèi)太多的時(shí)間會(huì)降低開發(fā)效率,因此筆...
摘要:指示該錯(cuò)誤是否嚴(yán)重,此屬性會(huì)在該異常根據(jù)錯(cuò)誤的上下文遍歷堆棧時(shí)進(jìn)行更新,嚴(yán)重性會(huì)指示異常捕獲代碼是應(yīng)該停止程序還是該繼續(xù)處理。引發(fā)異常在檢測(cè)到錯(cuò)誤并無法從中恢復(fù)時(shí),異常將向上傳播到調(diào)用堆棧,直到到達(dá)處理它的某個(gè)塊。 翻譯:瘋狂的技術(shù)宅 原文標(biāo)題:Exception handling strategy 原文鏈接:http://programmergate.com/exc...本文首發(fā)微信...
摘要:的處理流程就是在樹中的傳遞的過程這個(gè)過程分為步第一步,在樹中尋找處理的第二步,剩余的在樹傳遞給目標(biāo)第一步,在樹中尋找處理的遞歸方式完成從頂向下傳遞,找到到的最底層的從底向上,查找可以處理的并記錄從到的路徑其中涉及到以及遞歸的方式調(diào)用的尋找最 TouchEvent的處理流程就是TouchEvent在View樹中的傳遞的過程:這個(gè)過程分為2步:第一步,ACTION_DOWN在View樹中尋...
閱讀 3165·2021-11-24 10:47
閱讀 3929·2021-11-02 14:43
閱讀 2323·2021-09-26 10:15
閱讀 2694·2021-09-08 09:35
閱讀 642·2019-08-30 12:45
閱讀 2841·2019-08-29 17:04
閱讀 3342·2019-08-26 14:05
閱讀 1365·2019-08-26 12:10