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