NullPointerException 是當您嘗試使用指向記憶體中任何位置 (null) 的參考時發生的異常,就像引用物件一樣。對空引用呼叫方法或嘗試存取空引用的欄位將觸發 NullPointerException。這些是最常見的,但 中列出了其他方法NullPointerException# javadoc 頁面。
NullPointerException
我能想到的用來說明 NullPointerException 的最快範例程式碼可能是:
public class Example { public static void main(String[] args) { Object obj = null; obj.hashCode(); } }
在 main 內的第一行,我明確地將 Object 引用 obj 設為 null 。這意味著我有一個引用,但它沒有指向任何物件。之後,我嘗試透過呼叫該引用的方法來將該引用視為指向一個物件。這會導致 NullPointerException,因為在引用指向的位置沒有要執行的程式碼。
main
Object
obj
null
(這是一個技術問題,但我認為值得一提的是:指向null 的引用與指向無效記憶體位置的C 指標不同。空指標實際上不指向任何地方,這與指向一個恰好無效的位置有細微的不同。)
Java 中有兩種主要類型的變數:
基元:包含資料的變數。如果您想操作原始變數中的數據,您可以直接操作該變數。按照慣例,原始類型以小寫字母開頭。例如,int 或 char 類型的變數是基元。
int
char
引用:包含物件記憶體位址的變量,即引用物件的變數程式碼>.如果您想要操作引用變數引用的Object,則必須取消引用它。取消引用通常需要使用 . 存取方法或字段,或使用 [ 索引數組。按照慣例,引用類型通常用以大寫字母開頭的類型來表示。例如,Object 類型的變數是引用。
物件
.
[
考慮以下程式碼,您在其中聲明 int 類型的原始變量,但不初始化它:
int x; int y = x + x;
這兩行會使程式崩潰,因為沒有為 x 指定任何值,而我們正在嘗試使用 x 的值來指定 y >。所有基元在被操作之前都必須初始化為可用值。
x
y
現在事情變得有趣了。 引用變數可以設定為null,這表示「我沒有引用任何東西」。如果您以這種方式明確設定引用變量,則可以在引用變數中取得null 值,或引用變數未初始化且編譯器不會擷取它(Java 會自動將該變數設為null )。
如果您明確或透過 Java 自動將參考變數設為 null,並且您嘗試取消引用它,您將得到一個 NullPointerException。
當您宣告一個變數但在嘗試使用該變數的內容之前沒有建立物件並將其指派給該變數時,通常會發生NullPointerException (NPE)。所以你引用了一些實際上並不存在的東西。
採用以下程式碼:
Integer num; num = new Integer(10);
第一行宣告了一個名為num的變量,但它實際上還不包含引用值。由於您還沒有說出要指向什麼,Java 將其設為 null。
num
第二行,new關鍵字用於實例化(或建立)一個Integer類型的對象,引用變數num 被指派給該Integer 物件。
new
Integer
如果您在建立物件之前嘗試取消參考num,,您將得到一個NullPointerException。在最簡單的情況下,編譯器會捕獲問題並讓您知道“num 可能尚未初始化”,但有時您可能會編寫不直接建立物件的程式碼。
num 可能尚未初始化
例如,您可能有以下方法:
public void doSomething(SomeObject obj) { // Do something to obj, assumes obj is not null obj.myMethod(); }
在這種情況下,您不會建立物件 obj,而是假設它是在呼叫 doSomething() 方法之前建立的。請注意,可以像這樣呼叫該方法:
doSomething()
doSomething(null);
在這種情況下,obj 為null,並且語句obj.myMethod() 會拋出NullPointerException >.
obj.myMethod()
如果該方法打算像上面的方法那樣對傳入的物件執行某些操作,則拋出NullPointerException 是適當的,因為這是程式設計師錯誤,而程式設計師需要該資訊偵錯目的。
除了由於方法邏輯引發的NullPointerException 例外之外,您還可以檢查方法參數中的null 值,並透過新增類似以下內容來明確拋出NPE:在方法開頭附近跟隨:
// Throws an NPE with a custom error message if obj is null Objects.requireNonNull(obj, "obj must not be null");
請注意,在錯誤訊息中明確說明哪個物件不能為null會很有幫助。驗證這一點的優點是 1) 您可以返回自己更清晰的錯誤訊息,2) 對於方法的其餘部分,您知道除非重新分配 obj ,否則它不為 null 並且可以安全地取消引用.
或者,在某些情況下,該方法的目的不僅僅是對傳入的物件進行操作,因此空參數可能是可接受的。在這種情況下,您需要檢查空參數並採取不同的行為。您還應該在文件中對此進行解釋。例如,doSomething() 可以寫成:
/** * @param obj An optional foo for ____. May be null, in which case * the result will be ____. */ public void doSomething(SomeObject obj) { if(obj == null) { // Do something } else { // Do something else } }
最後,如何使用堆疊追蹤來找出異常和原因
具有查找錯誤功能的聲納可以偵測 NPE。 sonar能否動態擷取JVM所造成的空指標例外一个>
現在 Java 14 新增了一項新的語言功能來顯示 NullPointerException 的根本原因。自 2006 年以來,此語言功能已成為 SAP 商業 JVM 的一部分。
在 Java 14 中,以下是 NullPointerException 例外訊息範例:
以下是 Java 語言規範直接*提到的發生 NullPointerException 的所有情況:
拋出空值;
synchronized (someNullReference) { ... }
super
class Outer { class Inner {} } class ChildOfInner extends Outer.Inner { ChildOfInner(Outer o) { o.super(); // if o is null, NPE gets thrown } }
使用 for (element : iterable) 迴圈來循環遍歷空集合/陣列。
for (element : iterable)
switch (foo) { ... }(無論是表達式或語句)在foo 時可以拋出NullPointerException#為空。
switch (foo) { ... }
foo
foo.new SomeInnerClass() 當 foo 為 null 時拋出 NullPointerException。
foo.new SomeInnerClass()
name1::name2 或primaryExpression::name# 形式的方法參考在下列情況下求值時會拋出NullPointerException name1 或primaryExpression 計算結果為null。
name1::name2
primaryExpression::name
primaryExpression
來自JLS 的註解指出,someInstance.someStaticMethod() 不會拋出NPE,因為someStaticMethod 是靜態的,但someInstance:: someStaticMethod 仍然拋出NPE!
someInstance.someStaticMethod()
someStaticMethod
someInstance:: someStaticMethod
* 請注意,JLS 可能也間接談論了很多有關 NPE 的內容。
NullPointerException
是當您嘗試使用指向記憶體中任何位置 (null) 的參考時發生的異常,就像引用物件一樣。對空引用呼叫方法或嘗試存取空引用的欄位將觸發NullPointerException
。這些是最常見的,但 中列出了其他方法NullPointerException
# javadoc 頁面。我能想到的用來說明 NullPointerException 的最快範例程式碼可能是:
在
main
內的第一行,我明確地將Object
引用obj
設為null
。這意味著我有一個引用,但它沒有指向任何物件。之後,我嘗試透過呼叫該引用的方法來將該引用視為指向一個物件。這會導致NullPointerException
,因為在引用指向的位置沒有要執行的程式碼。(這是一個技術問題,但我認為值得一提的是:指向null 的引用與指向無效記憶體位置的C 指標不同。空指標實際上不指向任何地方,這與指向一個恰好無效的位置有細微的不同。)
Java 中有兩種主要類型的變數:
基元:包含資料的變數。如果您想操作原始變數中的數據,您可以直接操作該變數。按照慣例,原始類型以小寫字母開頭。例如,
int
或char
類型的變數是基元。引用:包含
物件
記憶體位址的變量,即引用物件的變數程式碼>.如果您想要操作引用變數引用的Object
,則必須取消引用它。取消引用通常需要使用.
存取方法或字段,或使用[
索引數組。按照慣例,引用類型通常用以大寫字母開頭的類型來表示。例如,Object
類型的變數是引用。考慮以下程式碼,您在其中聲明
int
類型的原始變量,但不初始化它:這兩行會使程式崩潰,因為沒有為
x
指定任何值,而我們正在嘗試使用x
的值來指定y
>。所有基元在被操作之前都必須初始化為可用值。現在事情變得有趣了。 引用變數可以設定為
null
,這表示「我沒有引用任何東西」。如果您以這種方式明確設定引用變量,則可以在引用變數中取得null
值,或引用變數未初始化且編譯器不會擷取它(Java 會自動將該變數設為null
)。如果您明確或透過 Java 自動將參考變數設為 null,並且您嘗試取消引用它,您將得到一個
NullPointerException
。當您宣告一個變數但在嘗試使用該變數的內容之前沒有建立物件並將其指派給該變數時,通常會發生
NullPointerException
(NPE)。所以你引用了一些實際上並不存在的東西。採用以下程式碼:
第一行宣告了一個名為
num
的變量,但它實際上還不包含引用值。由於您還沒有說出要指向什麼,Java 將其設為null
。第二行,
new
關鍵字用於實例化(或建立)一個Integer
類型的對象,引用變數num
被指派給該Integer
物件。如果您在建立物件之前嘗試取消參考
num
,,您將得到一個NullPointerException
。在最簡單的情況下,編譯器會捕獲問題並讓您知道“num 可能尚未初始化
”,但有時您可能會編寫不直接建立物件的程式碼。例如,您可能有以下方法:
在這種情況下,您不會建立物件
obj
,而是假設它是在呼叫doSomething()
方法之前建立的。請注意,可以像這樣呼叫該方法:在這種情況下,
obj
為null
,並且語句obj.myMethod()
會拋出NullPointerException
>.如果該方法打算像上面的方法那樣對傳入的物件執行某些操作,則拋出
NullPointerException
是適當的,因為這是程式設計師錯誤,而程式設計師需要該資訊偵錯目的。除了由於方法邏輯引發的
NullPointerException
例外之外,您還可以檢查方法參數中的null
值,並透過新增類似以下內容來明確拋出NPE:在方法開頭附近跟隨:請注意,在錯誤訊息中明確說明哪個物件不能為
null
會很有幫助。驗證這一點的優點是 1) 您可以返回自己更清晰的錯誤訊息,2) 對於方法的其餘部分,您知道除非重新分配 obj ,否則它不為 null 並且可以安全地取消引用.或者,在某些情況下,該方法的目的不僅僅是對傳入的物件進行操作,因此空參數可能是可接受的。在這種情況下,您需要檢查空參數並採取不同的行為。您還應該在文件中對此進行解釋。例如,
doSomething()
可以寫成:最後,如何使用堆疊追蹤來找出異常和原因
#具有查找錯誤功能的聲納可以偵測 NPE。 sonar能否動態擷取JVM所造成的空指標例外一个>
現在 Java 14 新增了一項新的語言功能來顯示 NullPointerException 的根本原因。自 2006 年以來,此語言功能已成為 SAP 商業 JVM 的一部分。
在 Java 14 中,以下是 NullPointerException 例外訊息範例:
導致
NullPointerException
發生的情況清單以下是 Java 語言規範直接*提到的發生
NullPointerException
的所有情況:拋出空值;
synchronized (someNullReference) { ... }
NullPointerException
NullPointerException
。super
會引發NullPointerException
。如果您感到困惑,這是在談論合格的超類別建構函式呼叫:使用
for (element : iterable)
迴圈來循環遍歷空集合/陣列。switch (foo) { ... }
(無論是表達式或語句)在foo
時可以拋出NullPointerException
#為空。foo.new SomeInnerClass()
當foo
為 null 時拋出NullPointerException
。name1::name2
或primaryExpression::name
# 形式的方法參考在下列情況下求值時會拋出NullPointerException
name1 或primaryExpression
計算結果為null。來自JLS 的註解指出,
someInstance.someStaticMethod()
不會拋出NPE,因為someStaticMethod
是靜態的,但someInstance:: someStaticMethod
仍然拋出NPE!* 請注意,JLS 可能也間接談論了很多有關 NPE 的內容。