在Java語言在,null被分配給一個對象的引用以表示對象指向未知數據塊。當應用程序使用或訪問一個指向null的引用,會被拋出。
下列情況會拋出NullPointerException 。
- 調用null對象的方法。
- 訪問或修改null對象的域。
- 如果null是一個數組,並獲取null的長度。
- 如果null對象是一個對象數組,並訪問會修改null對象的子元素。
- 如果對象是一個 Throwable值,並拋出null。
- 試圖對null對象同步。
NullPointerException 是一個 RuntimeException ,Javac編譯器不會強制你使用try-catch塊來處理該異常。
爲什麼我們需要null值?
如前所述,null是一個Java中的特殊值。null在編寫一些設計模式時非常有用。例如Null對象模式[1] 與單例模式。Null對象模式提供一個對象作爲缺少給定類型對象的代理。單例模式確保只有一個類的實例被創建,並且提供一個對象的全局訪問點。
例如,至多創建類的一個實例的一種簡單的方法是聲明類的所有構造函數給私有類型,並創建一個公共方法,該方法返回這個類的唯一實例。
TestSingleton.java:
import java.util.UUID;
class Singleton {
private static Singleton single = null;
private String ID = null;
private Singleton() {
/* Make it private, in order to prevent the creation of new instances of
* the Singleton class. */
ID = UUID.randomUUID().toString(); // Create a random ID.
}
public static Singleton getInstance() {
if (single == null)
single = new Singleton();
return single;
}
public String getID() {
return this.ID;
}
}
public class TestSingleton {
public static void main(String[] args) {
Singleton s = Singleton.getInstance();
System.out.println(s.getID());
}
在這個例子中,我們聲明瞭一個Singletion類的靜態實例。這個實例被在getInstance 方法中被初始化一次。注意,使用null值能使我們能夠創建唯一的實例。
如何避免空NullPointerException
爲了避免NullPointerException,確保所有對象在你使用之前被初始化。注意,當你聲明一個變量的引用時,你正在創建一個指向一個對象的指針。在你使用該對象的方法或域之前,你必須檢驗指針不爲null。
如果一個異常被拋出,使用在異常棧軌跡中駐留的信息。執行程序的棧軌跡由JVM提供,爲了能夠調試應用程序。
1. 帶有字面值的字符串比較
在應用程序代碼中一種常見的情況是將字符串變量與字面值進行比較。字符串字面值可能是字符串或枚舉元素。我們將通過使用字面值來調用方法,而不是通過使用null對象來調用方法。例如,觀察下面的例子:
String str = null;
if(str.equals("Test")) {
/* The code here will not be reached, as an exception will be thrown. */
}
上面的代碼片段會拋出NullPointerException。然後,如果通過字面值來調用方法,程序會正常執行。
String str = null;
if("Test".equals(str)) {
/* Correct use case. No exception will be thrown. */
}
2.檢查一個方法的參數
在執行方法之前,確保檢查了參數是否爲null.當參數被適當檢查後,方法會繼續執行。否則,你可以拋出IllegalArgumentException並且通知調用方法傳入的參數有誤。
public static int getLength(String s) {
if (s == null)
throw new IllegalArgumentException("The argument cannot be null");
return s.length();
}
3 優先使用String.valueOf()而不是toString()
當你的應用程序代碼如要一個對象的字符串來描述時,避免使用對象的toString方法。如果你的對象的引用爲null,NullPointerException將會被拋出。反之,考慮使用靜態方法String.valueOf(),該方法不會拋出任何異常並且在函數參數爲null的情況下會打印null。
4. 使用三元運算符
三元運算符能幫助我們避免NullPointerException.運算符具有這樣的形式:
boolean expression ? value1 : value2;
三元運算符能幫助我們避免NullPointerException.運算符具有這樣的形式:首先,計算布爾表達式,如果表達式爲true,value1被返回,否則value2被返回。我們能使用三元運算符來處理null指針,例如:
String message = (str == null) ? “” : str.substring(0, 10);
變量message將爲空,如果str的引用爲null,否則,如果str指向實際的數據,message將獲取str的前10個字符。
5.創建返回空集合而不是null的方法
一種非常好的技術是創建一個返回空集合的方法,而不是返回null值。你的應用程序代碼可以迭代空集合並使用它的方法和域,而不會拋出NullPointerException。例如:
public class Example {
private static List<Integer> numbers = null;
public static List<Integer> getList() {
if (numbers == null)
return Collections.emptyList();
else
return numbers;
}
}
6.利用Apache的 StringUtils類
Apache的Commons Lang是一個庫,爲java.lang API提供了幫助工具,例如字符串操作方法。StringUtils.java提供了字符串的操作,該類處理字符串對象爲null的情況。你可以使用StringUtils.isNotEmpty, StringUtils.IsEmpty 及 StringUtils.equals 方法,以避免NullPointerException。
7. 使用contains(), containsKey(), containsValue() 方法
如果你的程序使用了像Maps這樣的集合,考慮使用contains(), containsKey(), containsValue()方法。例如,在驗證某些鍵存在與Map中時,返回特定鍵的值。
Map<String, String> map = …
…
String key = …
String value = map.get(key);
System.out.println(value.toString()); // An exception will be thrown, if the value is null.
在上面的片段中,我們不檢查鍵是否存在與Map中,返回值可能爲null.最安全的方式是:
Map<String, String> map = …
…
String key = …
if(map.containsKey(key)) {
String value = map.get(key);
System.out.println(value.toString()); // No exception will be thrown.
}
8.檢查外部方法的返回值
實際環境中,使用外部的庫很常見。這些庫包含返回某個引用的方法。確保返回的引用不爲null.閱讀javadoc的方法,以更好理解函數功能與返回值。
9.使用斷言
當測試代碼時,斷言很有用。以避免執行會拋出NullPointerException的方法。Java斷言的實現通過assert關鍵字並拋出AssertionError.注意,你必須顯式通過-ea參數啓用斷言。否則斷言將會被忽略。
public static int getLength(String s) {
/* Ensure that the String is not null. */
assert (s != null);
return s.length();
}
如果你執行上述代碼並傳遞null參數給getLength,將會出現下面的錯誤。
最後,你可以使用由Junit測試框架提供的Assert類。
存在NullPointerException的安全方法
1.訪問靜態成員或類方法
當你的代碼試圖訪問靜態變量或一個類的方法,即使對象的引用等於null,JVM也不會拋出NullPointerException.這是因爲,在編過程中,Java編譯器存儲靜態方法和域在特殊的位置。靜態方法和域不與對象關聯,而是與類名關聯。
例如下面的代碼不會拋出NullPointerException.
class SampleClass {
public static void printMessage() {
System.out.println("Hello from Java Code Geeks!");
}
}
public class TestStatic {
public static void main(String[] args) {
SampleClass sc = null;
sc.printMessage();
}
}
注意,儘管SampleClass的實例爲null,方法還是會被執行。當方法或域爲靜態時,應該以“靜態”的方式來訪問,即通過類名來訪問。例如:SampleClass.printMessage()
2. instanceof 操作符
即使對象的引用爲null,instanceof操作符可使用。當引用爲null時,instanceof 操作符返回false,而且不會拋出NullPointerException.例如,下面的代碼:
String str = null;
if(str instanceof String)
System.out.println("It's an instance of the String class!");
else
System.out.println("Not an instance of the String class!");
結果如下:
Not an instance of the String class!