1. 程式人生 > >Java中NullPointerException異常的原因詳解以及解決方法

Java中NullPointerException異常的原因詳解以及解決方法

NullPointerException是當您嘗試使用指向記憶體中空位置的引用(null)時發生的異常,就好像它引用了一個物件一樣。

當我們宣告引用變數(即物件)時,實際上是在建立指向物件的指標。考慮以下程式碼,您可以在其中宣告基本型別的整型變數x:

int x;
x = 10;

在此示例中,變數x是一個整型變數,Java將為您初始化為0。當您在第二行中將其分配給10時,值10將被寫入x指向的記憶體中。

但是,當您嘗試宣告引用型別時會發生不同的事情。請使用以下程式碼:

Integer num;
num = new Integer(10);

第一行聲明瞭一個名為的變數num,但它不包含原始值。相反,它包含一個指標(因為型別Integer

是一個引用型別)。既然你還沒有說什麼指向Java,它將它設定為null,意思是“ 我什麼都沒有指向”。

在第二行中,new關鍵字用於例項化(或建立)Integer型別的物件,併為指標變數num分配此物件。您現在可以使用解引用運算子.(點)來引用物件。

在當你聲明瞭一個變數,但是沒有建立一個物件,會發生Exception。如果您在建立num物件之前嘗試取消引用,則會得到一個NullPointerException。在最瑣碎的情況下,編譯器將捕獲問題並讓您知道“num可能尚未初始化”,但有時您編寫的程式碼不會直接建立物件。

例如,您可能使用瞭如下的方法:

public void doSomething(SomeObject obj) {
   //do something to obj
}

在這種情況下,您沒有建立物件obj,而是假設它是在doSomething呼叫方法之前建立的。如果你像這樣呼叫方法:

doSomething(null);

在這種情況下obj為null。如果該方法旨在對傳入的物件執行某些操作,則需要丟擲異常,因為NullPointerException它是程式錯誤,程式設計師將需要該資訊用於除錯的目的。

或者,可能存在這樣的情況:該方法的目的不僅僅是對傳入的物件進行操作,因此可以接受空引數。在這種情況下,您需要檢查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
    }
}

 

我如何解決它?

所以你有一個NullPointerException。應該如何解決?讓我們舉一個簡單的例子,它丟擲NullPointerException

public class Printer {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer();
        printer.print();
    }
}

 

標識空值

第一步是確切地確定導致異常的值。為此,我們需要做一些除錯。學習閱讀堆疊跟蹤很重要。這將顯示丟擲異常的位置:

Exception in thread "main" java.lang.NullPointerException
    at Printer.printString(Printer.java:13)
    at Printer.print(Printer.java:9)
    at Printer.main(Printer.java:19)

在這裡,我們看到在第13行丟擲異常(在printString方法中)。檢視該行並通過新增日誌記錄語句或使用偵錯程式來檢查哪些值為空。我們發現它s是null,並且呼叫length方法會丟擲異常。我們可以看到程式在s.length()方法中刪除時停止丟擲異常。

追蹤這些值來自哪裡

接下來檢查此值的來源。按照該方法的呼叫者,我們可以看到,s與傳遞printString(name)print()方法,並this.name為空。

跟蹤應設定這些值的位置

在哪裡this.name設定?在setName(String)方法中。通過一些更多的除錯,我們可以看到根本沒有呼叫此方法。如果呼叫該方法,請確保檢查呼叫這些方法的順序,並且在print方法之後不呼叫set 方法。

這足以為我們提供一個解決方案:在呼叫printer.setName()之前新增呼叫printer.print()

 

其他修正

該變數可以具有預設值(並且setName可以防止將其設定為null):

private String name = "";

任一printprintString方法可以檢查空,例如:

printString((name == null) ? "" : name);

或者您可以設計如下所示的類,以便name 始終具有非null值

public class Printer {
    private final String name;

    public Printer(String name) {
        this.name = Objects.requireNonNull(name);
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer("123");
        printer.print();
    }
}