什麼是 NullPointerException,如何修復它?
P粉649990273
P粉649990273 2023-08-23 10:22:08
0
2
614
<p>什麼是空指標例外 (<code>java.lang.NullPointerException</code>) 以及導致它們的原因是什麼? </p> <p>可以使用哪些方法/工具來決定原因,以便阻止異常導致程式提前終止? </p>
P粉649990273
P粉649990273

全部回覆(2)
P粉252116587

NullPointerException 是當您嘗試使用指向記憶體中任何位置 (null) 的參考時發生的異常,就像引用物件一樣。對空引用呼叫方法或嘗試存取空引用的欄位將觸發 NullPointerException。這些是最常見的,但 中列出了其他方法NullPointerException# javadoc 頁面。

我能想到的用來說明 NullPointerException 的最快範例程式碼可能是:

public class Example {

    public static void main(String[] args) {
        Object obj = null;
        obj.hashCode();
    }

}

main 內的第一行,我明確地將 Object 引用 obj 設為 null 。這意味著我有一個引用,但它沒有指向任何物件。之後,我嘗試透過呼叫該引用的方法來將該引用視為指向一個物件。這會導致 NullPointerException,因為在引用指向的位置沒有要執行的程式碼。

(這是一個技術問題,但我認為值得一提的是:指向null 的引用與指向無效記憶體位置的C 指標不同。空指標實際上不指向任何地方,這與指向一個恰好無效的位置有細微的不同。)

P粉593536104

Java 中有兩種主要類型的變數:

  1. 基元:包含資料的變數。如果您想操作原始變數中的數據,您可以直接操作該變數。按照慣例,原始類型以小寫字母開頭。例如,intchar 類型的變數是基元。

  2. 引用:包含物件記憶體位址的變量,即引用物件的變數程式碼>.如果您想要操作引用變數引用的Object,則必須取消引用它。取消引用通常需要使用 . 存取方法或字段,或使用 [ 索引數組。按照慣例,引用類型通常用以大寫字母開頭的類型來表示。例如,Object 類型的變數是引用。

考慮以下程式碼,您在其中聲明 int 類型的原始變量,但不初始化它:

int x;
int y = x + x;

這兩行會使程式崩潰,因為沒有為 x 指定任何值,而我們正在嘗試使用 x 的值來指定 y >。所有基元在被操作之前都必須初始化為可用值。

現在事情變得有趣了。 引用變數可以設定為null,這表示「我沒有引用任何東西」。如果您以這種方式明確設定引用變量,則可以在引用變數中取得null 值,或引用變數未初始化且編譯器不會擷取它(Java 會自動將該變數設為null )。

如果您明確或透過 Java 自動將參考變數設為 null,並且您嘗試取消引用它,您將得到一個 NullPointerException

當您宣告一個變數但在嘗試使用該變數的內容之前沒有建立物件並將其指派給該變數時,通常會發生NullPointerException (NPE)。所以你引用了一些實際上並不存在的東西。

採用以下程式碼:

Integer num;
num = new Integer(10);

第一行宣告了一個名為num的變量,但它實際上還不包含引用值。由於您還沒有說出要指向什麼,Java 將其設為 null

第二行,new關鍵字用於實例化(或建立)一個Integer類型的對象,引用變數num 被指派給該Integer 物件。

如果您在建立物件之前嘗試取消參考num,您將得到一個NullPointerException。在最簡單的情況下,編譯器會捕獲問題並讓您知道“num 可能尚未初始化”,但有時您可能會編寫不直接建立物件的程式碼。

例如,您可能有以下方法:

public void doSomething(SomeObject obj) {
   // Do something to obj, assumes obj is not null
   obj.myMethod();
}

在這種情況下,您不會建立物件 obj,而是假設它是在呼叫 doSomething() 方法之前建立的。請注意,可以像這樣呼叫該方法:

doSomething(null);

在這種情況下,objnull,並且語句obj.myMethod() 會拋出NullPointerException >.

如果該方法打算像上面的方法那樣對傳入的物件執行某些操作,則拋出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 例外訊息範例:

導致 NullPointerException 發生的情況清單

以下是 Java 語言規範直接*提到的發生 NullPointerException 的所有情況:

  • 存取(即取得或設定)空引用的實例欄位。 (靜態字段不算!)
  • 呼叫空引用的實例方法。 (靜態方法不算!)
  • 拋出空值;
  • #存取空數組的元素。
  • 在 null 同步 - synchronized (someNullReference) { ... }
  • 如果其運算元之一是裝箱空引用,則任何整數/浮點運算子都可能拋出 NullPointerException
  • 如果裝箱值為 null,則拆箱轉換會引發 NullPointerException
  • 對 null 引用呼叫 super 會引發 NullPointerException。如果您感到困惑,這是在談論合格的超類別建構函式呼叫:
class Outer {
    class Inner {}
}
class ChildOfInner extends Outer.Inner {
    ChildOfInner(Outer o) { 
        o.super(); // if o is null, NPE gets thrown
    }
}
  • 使用 for (element : iterable) 迴圈來循環遍歷空集合/陣列。

  • switch (foo) { ... }(無論是表達式或語句)在foo 時可以拋出NullPointerException#為空。

  • foo.new SomeInnerClass()foo 為 null 時拋出 NullPointerException

  • name1::name2primaryExpression::name# 形式的方法參考在下列情況下求值時會拋出NullPointerException name1 或primaryExpression 計算結果為null。

    來自JLS 的註解指出,someInstance.someStaticMethod() 不會拋出NPE,因為someStaticMethod 是靜態的,但someInstance:: someStaticMethod 仍然拋出NPE!

* 請注意,JLS 可能也間接談論了很多有關 NPE 的內容。

熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板