NullPulumException là gì và làm cách nào để khắc phục nó?


210

Null Pointer Exceptions (là gì java.lang.NullPointerException) và những gì khiến họ?

Những phương pháp / công cụ nào có thể được sử dụng để xác định nguyên nhân để bạn ngăn chặn ngoại lệ khiến chương trình chấm dứt sớm?

Câu trả lời:


3764

Khi bạn khai báo một biến tham chiếu (tức là một đối tượng), bạn thực sự đang tạo một con trỏ tới một đối tượng. Hãy xem xét đoạn mã sau nơi bạn khai báo một biến kiểu nguyên thủy int:

int x;
x = 10;

Trong ví dụ này, biến xlà một intvà Java sẽ khởi tạo nó 0cho bạn. Khi bạn gán cho nó giá trị của 10dòng thứ hai, giá trị của bạn 10được ghi vào vị trí bộ nhớ được gọi bởi x.

Nhưng, khi bạn cố gắng khai báo một loại tham chiếu , một cái gì đó khác nhau xảy ra. Lấy mã sau:

Integer num;
num = new Integer(10);

Dòng đầu tiên khai báo một biến có tên num, nhưng nó không thực sự chứa giá trị nguyên thủy. Thay vào đó, nó chứa một con trỏ (vì kiểu này Integerlà kiểu tham chiếu). Vì bạn chưa nói phải chỉ đến cái gì, Java đặt nó vào null, có nghĩa là " Tôi đang chỉ vào không có gì ".

Trong dòng thứ hai, newtừ khóa được sử dụng để khởi tạo (hoặc tạo) một đối tượng kiểu Integervà biến con trỏ numđược gán cho Integerđối tượng đó .

Điều NullPointerExceptionnày xảy ra khi bạn khai báo một biến nhưng không tạo ra một đối tượng và gán cho biến đó trước khi cố gắng sử dụng nội dung của biến đó (được gọi là dereferences ). Vì vậy, bạn đang chỉ vào một cái gì đó không thực sự tồn tại.

Dereferences thường xảy ra khi sử dụng .để truy cập một phương thức hoặc trường hoặc sử dụng [để lập chỉ mục một mảng.

Nếu bạn cố gắng hủy đăng ký numTRƯỚC KHI tạo đối tượng bạn nhận được a NullPointerException. Trong các trường hợp tầm thường nhất, trình biên dịch sẽ nắm bắt được vấn đề và cho bạn biết rằng " num may not have been initialized," nhưng đôi khi bạn có thể viết mã không trực tiếp tạo đối tượng.

Chẳng hạn, bạn có thể có một phương pháp như sau:

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

Trong trường hợp đó, bạn không tạo đối tượng obj, mà là giả sử rằng nó đã được tạo trước khi doSomething()phương thức được gọi. Lưu ý, có thể gọi phương thức như thế này:

doSomething(null);

Trong trường hợp này, objnull. Nếu phương thức được dự định để làm một cái gì đó cho đối tượng truyền vào, thì phù hợp để ném NullPointerExceptionvì đó là lỗi lập trình viên và lập trình viên sẽ cần thông tin đó cho mục đích gỡ lỗi. Vui lòng bao gồm tên của biến đối tượng trong thông báo ngoại lệ, như

Objects.requireNonNull(a, "a");

Ngoài ra, có thể có trường hợp mục đích của phương thức không chỉ hoạt động trên đối tượng được truyền vào, và do đó, một tham số null có thể được chấp nhận. Trong trường hợp này, bạn sẽ cần kiểm tra tham số null và hành xử khác đi. Bạn cũng nên giải thích điều này trong tài liệu. Ví dụ: doSomething()có thể được viết là:

/**
  * @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ối cùng, Cách xác định ngoại lệ & nguyên nhân bằng cách sử dụng Stack Trace

Những phương pháp / công cụ nào có thể được sử dụng để xác định nguyên nhân để bạn ngăn chặn ngoại lệ khiến chương trình chấm dứt sớm?

Sonar với findbugs có thể phát hiện NPE. Sonar có thể bắt ngoại lệ con trỏ null do JVM tự động không


558
"Cách tốt nhất để tránh loại ngoại lệ này là luôn kiểm tra null khi bạn không tự tạo đối tượng." Nếu người gọi vượt qua null, nhưng null không phải là một đối số hợp lệ cho phương thức, thì việc trả lại ngoại lệ cho người gọi là đúng vì đó là lỗi của người gọi. Âm thầm bỏ qua đầu vào không hợp lệ và không làm gì trong phương pháp là lời khuyên cực kỳ tồi vì nó che giấu vấn đề.
Boann

104
Tôi sẽ thêm một nhận xét về bài đăng này giải thích rằng ngay cả các bài tập cho người nguyên thủy cũng có thể gây ra NPE khi sử dụng autoboxing: int a=bcó thể ném NPE nếu b là một Integer. Có những trường hợp điều này là khó hiểu để gỡ lỗi.
Simon Fischer

58
Có thể chụp NPE được ném bởi một ứng dụng web từ trình duyệt web không? Giống như nó sẽ hiển thị trong nguồn trang xem từ trình duyệt web ..
Sid

76
Có kiểm tra xem đối tượng có bằng null không trước khi bạn gọi một phương thức trên nó hoặc thử truy cập vào một biến mà nó có thể có. Đôi khi cấu trúc mã của bạn có thể giúp tránh ngoại lệ con trỏ null. vd Vì vậy, có một loạt những điều bạn có thể làm để cố gắng an toàn.
Hoa hồng

78
Một cách khác để tránh NullPointerExceptioncác vấn đề trong mã của bạn là sử dụng @Nullable@NotNullchú thích. Câu trả lời sau đây có thêm thông tin về điều này. Mặc dù câu trả lời này là cụ thể về IntelliJ IDE, nhưng nó cũng có thể áp dụng cho các công cụ khác như được hiển thị từ các bình luận. (BTW Tôi không được phép chỉnh sửa câu trả lời này trực tiếp, có lẽ tác giả có thể thêm nó không?)
Arjan Mels

879

NullPointerExceptions là những ngoại lệ xảy ra khi bạn cố gắng sử dụng một tham chiếu trỏ đến không có vị trí nào trong bộ nhớ (null) như thể nó đang tham chiếu một đối tượng. Gọi một phương thức trên tham chiếu null hoặc cố gắng truy cập vào trường của tham chiếu null sẽ kích hoạt a NullPointerException. Đây là những cách phổ biến nhất, nhưng các cách khác được liệt kê trên NullPointerExceptiontrang javadoc.

Có lẽ mã ví dụ nhanh nhất tôi có thể đưa ra để minh họa NullPointerExceptionsẽ là:

public class Example {

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

}

Trên dòng đầu tiên bên trong main, tôi rõ ràng đặt Objecttham chiếu objbằng null. Điều này có nghĩa là tôi có một tài liệu tham khảo, nhưng nó không trỏ đến bất kỳ đối tượng nào. Sau đó, tôi cố gắng xử lý tham chiếu như thể nó trỏ đến một đối tượng bằng cách gọi một phương thức trên nó. Điều này dẫn đến một NullPointerExceptionvì không có mã để thực thi tại vị trí mà tham chiếu đang trỏ.

(Đây là một kỹ thuật, nhưng tôi nghĩ rằng nó có đề cập đến: Một tham chiếu trỏ đến null không giống như một con trỏ C trỏ đến một vị trí bộ nhớ không hợp lệ. Một con trỏ null thực sự không chỉ ra bất cứ nơi nào , khác biệt một cách tinh tế chỉ đến một vị trí không hợp lệ.)


49
Tôi hiểu mọi thứ bạn viết ở đó, nhưng chỉ bởi vì tôi đã viết mã được một lúc và biết 'con trỏ' và 'tham chiếu' là gì (và null là gì, đối với vấn đề đó). Khi tôi cố gắng đi sâu vào những lời giải thích như thế, các học sinh của tôi nhìn tôi chao đảo, vì không có đủ nền tảng.
mmr

33
@mmr: Cảm ơn phản hồi, bạn đã đưa ra quan điểm hợp lệ. Trên mạng thật khó để đánh giá ai đó đang ở đâu, và ở mức độ nào thì an toàn để bắt đầu một lời giải thích. Tôi sẽ thử sửa đổi điều này một lần nữa.
Bill Lizard

22
Một cách phổ biến hơn để có được NullPulumException trong thực tế sẽ quên không khởi tạo một cách rõ ràng một biến thành viên thành một thứ khác hơn là nulltrước khi sử dụng nó, như thế này . Với các biến cục bộ, trình biên dịch sẽ bắt lỗi này, nhưng trong trường hợp này thì không. Có lẽ điều đó sẽ làm cho một bổ sung hữu ích cho câu trả lời của bạn?
Ilmari Karonen

6
@EJP Tôi nghĩ rằng điểm của bạn là hợp lệ, vì vậy tôi đã cập nhật câu trả lời để rõ ràng hơn và để tránh nói 'điểm thành null' nơi nó đã làm.
Steve Powell

5
@StevePowell Tôi đã chỉ ra từ lâu rằng tôi không muốn câu trả lời của mình thay đổi. Hãy tôn trọng ý định của tác giả ban đầu.
Bill the Lizard

696

NullPulumException là gì?

Một nơi tốt để bắt đầu là JavaDocs . Họ có điều này được bảo hiểm:

Ném khi một ứng dụng cố gắng sử dụng null trong trường hợp bắt buộc phải có đối tượng. Bao gồm các:

  • Gọi phương thức thể hiện của một đối tượng null.
  • Truy cập hoặc sửa đổi trường của một đối tượng null.
  • Lấy chiều dài của null như thể nó là một mảng.
  • Truy cập hoặc sửa đổi các vị trí của null như thể nó là một mảng.
  • Ném null như thể nó là một giá trị Ném được.

Các ứng dụng nên ném các thể hiện của lớp này để chỉ ra việc sử dụng bất hợp pháp đối tượng null khác.

Đây cũng là trường hợp nếu bạn cố gắng sử dụng tham chiếu null với synchronized, điều đó cũng sẽ ném ngoại lệ này, theo JLS :

SynchronizedStatement:
    synchronized ( Expression ) Block
  • Mặt khác, nếu giá trị của Biểu thức là null, a NullPointerExceptionsẽ bị ném.

Làm thế nào để tôi sửa chữa nó?

Vì vậy, bạn có một NullPointerException. Làm thế nào để bạn sửa chữa nó? Hãy lấy một ví dụ đơn giản mà ném một 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();
    }
}

Xác định các giá trị null

Bước đầu tiên là xác định chính xác giá trị nào gây ra ngoại lệ . Đối với điều này, chúng ta cần phải làm một số gỡ lỗi. Điều quan trọng là học cách đọc một stacktrace . Điều này sẽ cho bạn thấy nơi ngoại lệ được ném:

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)

Ở đây, chúng ta thấy rằng ngoại lệ được ném vào dòng 13 (trong printStringphương thức). Nhìn vào dòng và kiểm tra giá trị nào là null bằng cách thêm các câu lệnh ghi nhật ký hoặc sử dụng trình gỡ lỗi . Chúng tôi phát hiện ra rằng nó slà null và gọilength phương thức trên nó sẽ ném ngoại lệ. Chúng ta có thể thấy rằng chương trình dừng ném ngoại lệ khi s.length()bị xóa khỏi phương thức.

Dấu vết nơi những giá trị này đến từ

Tiếp theo kiểm tra xem giá trị này đến từ đâu. Bằng cách theo dõi những người gọi phương thức, chúng ta thấy rằng sđược truyền vào printString(name)trong print()phương thức vàthis.name là null.

Theo dõi nơi các giá trị này nên được đặt

Đặt ở đâu this.name? Trong setName(String)phương pháp. Với một số gỡ lỗi khác, chúng ta có thể thấy rằng phương thức này hoàn toàn không được gọi. Nếu phương thức được gọi, hãy đảm bảo kiểm tra thứ tự các phương thức này được gọi và phương thức thiết lập không được gọi sau phương thức in.

Điều này là đủ để cung cấp cho chúng tôi một giải pháp: thêm một cuộc gọi đến printer.setName()trước khi gọiprinter.print() .

Các bản sửa lỗi khác

Biến có thể có giá trị mặc định (và setNamecó thể ngăn nó được đặt thành null):

private String name = "";

Hoặc là printhay printStringphương pháp có thể kiểm tra cho null , ví dụ:

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

Hoặc bạn có thể thiết kế lớp sao cho name luôn có giá trị khác 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();
    }
}

Xem thêm:

Tôi vẫn không thể tìm ra vấn đề

Nếu bạn đã cố gắng gỡ lỗi mà vẫn không có giải pháp, bạn có thể đăng câu hỏi để được trợ giúp thêm, nhưng hãy đảm bảo bao gồm những gì bạn đã thử cho đến nay. Tối thiểu, bao gồm stacktrace trong câu hỏi và đánh dấu các số dòng quan trọng trong mã. Ngoài ra, hãy thử đơn giản hóa mã trước tiên (xem SSCCE ).


44
+1 Tốt để có một ví dụ bao gồm việc đi qua stacktrace; điều quan trọng là chỉ ra tại sao việc đọc nó lại quan trọng để gỡ lỗi NPE. (và tại sao chúng ta hầu như luôn tìm kiếm một stacktrace khi ai đó đăng câu hỏi về lỗi)
Dennis Meng

16
Bạn đã đề cập đến việc gỡ lỗi ... Nó hoạt động như thế nào? Tôi đã nghiên cứu chủ đề này một thời gian, nhưng không thể tìm thấy gì. Tôi chắc rằng một giáo viên tuyệt vời như bạn có thể dạy nó cho tôi trong một giây! Cám ơn rất nhiều! :-)
Ruchir Baronia

15
@RuchirBaronia Trình gỡ lỗi cho phép bạn đi qua từng dòng chương trình để xem phương thức nào được gọi và cách thay đổi biến. IDE nên có một số công cụ để làm điều này. Xem vogella.com/tutorials/EclipseDebugging/article.html chẳng hạn.
fgb

15
@RuchirBaronia Bạn đặt các điểm dừng trên các phương thức xung quanh bất kỳ NullPulumExceptions như đã thấy trong stacktrace và kiểm tra các giá trị của các biến so với những gì bạn mong đợi. Nếu bạn biết một biến là null khi không nên, thì bạn có thể đặt các điểm dừng xung quanh bất kỳ mã nào thay đổi giá trị. Ngoài ra còn có các điểm dừng có điều kiện bạn có thể sử dụng sẽ cho bạn biết khi nào giá trị thay đổi.
fgb

6
Đặt các đối tượng Chuỗi thành một chuỗi trống làm giá trị mặc định của chúng được coi là một cách thực hành kém.
Tiny

501

Câu hỏi: Điều gì gây ra một NullPointerException(NPE)?

Như bạn nên biết, các loại Java được chia thành các loại nguyên thủy ( boolean, int, vv) và các loại tài liệu tham khảo . Các kiểu tham chiếu trong Java cho phép bạn sử dụng giá trị đặc biệt nulllà cách nói "không có đối tượng" của Java.

A NullPointerExceptionđược ném vào thời gian chạy bất cứ khi nào chương trình của bạn cố gắng sử dụng nullnhư thể nó là một tài liệu tham khảo thực sự. Ví dụ: nếu bạn viết điều này:

public class Test {
    public static void main(String[] args) {
        String foo = null;
        int length = foo.length();   // HERE
    }
}

câu lệnh được gắn nhãn "TẠI ĐÂY" sẽ cố gắng chạy length()phương thức trên một nulltham chiếu và điều này sẽ đưa ra một NullPointerException.

Có nhiều cách bạn có thể sử dụng null giá trị sẽ dẫn đến một NullPointerException. Trên thực tế, những điều duy nhất bạn có thể làm nullmà không gây ra NPE là:

  • gán nó cho một biến tham chiếu hoặc đọc nó từ một biến tham chiếu,
  • gán nó cho một phần tử mảng hoặc đọc nó từ một phần tử mảng (với điều kiện chính tham chiếu mảng đó là không null!),
  • vượt qua nó như một tham số hoặc trả lại kết quả, hoặc
  • kiểm tra nó bằng cách sử dụng ==hoặc!= toán tử, hoặc instanceof.

Câu hỏi: Làm thế nào để tôi đọc stacktrace NPE?

Giả sử rằng tôi biên dịch và chạy chương trình trên:

$ javac Test.java 
$ java Test
Exception in thread "main" java.lang.NullPointerException
    at Test.main(Test.java:4)
$

Quan sát đầu tiên: quá trình biên dịch thành công! Vấn đề trong chương trình KHÔNG phải là lỗi biên dịch. Nó là một thời gian chạy lỗi . (Một số IDE có thể cảnh báo chương trình của bạn sẽ luôn đưa ra một ngoại lệ ... nhưng javactrình biên dịch chuẩn không có.)

Quan sát thứ hai: khi tôi chạy chương trình, nó xuất ra hai dòng "gobbledy-gook". SAI LẦM!! Đó không phải là gobbledy-gook. Nó là một stacktrace ... và nó cung cấp thông tin quan trọng sẽ giúp bạn theo dõi lỗi trong mã của mình nếu bạn dành thời gian để đọc nó một cách cẩn thận.

Vì vậy, hãy nhìn vào những gì nó nói:

Exception in thread "main" java.lang.NullPointerException

Dòng đầu tiên của dấu vết ngăn xếp cho bạn biết một số điều:

  • Nó cho bạn biết tên của luồng Java trong đó ngoại lệ được ném ra. Đối với một chương trình đơn giản với một luồng (như cái này), nó sẽ là "chính". Tiếp tục nào ...
  • Nó cho bạn biết tên đầy đủ của ngoại lệ đã bị ném; tức java.lang.NullPointerException.
  • Nếu ngoại lệ có thông báo lỗi liên quan, đó sẽ là đầu ra sau tên ngoại lệ. NullPointerExceptionlà bất thường về mặt này, bởi vì nó hiếm khi có một thông báo lỗi.

Dòng thứ hai là dòng quan trọng nhất trong chẩn đoán NPE.

at Test.main(Test.java:4)

Điều này cho chúng ta biết một số điều:

  • "Tại Test.main" nói rằng chúng tôi đã ở trong mainphương thức của Testlớp.
  • "Test.java:4" cung cấp tên tệp nguồn của lớp, VÀ nó cho chúng ta biết rằng câu lệnh xảy ra trong dòng 4 của tệp.

Nếu bạn đếm các dòng trong tệp trên, dòng 4 là dòng mà tôi đã gắn nhãn với nhận xét "TẠI ĐÂY".

Lưu ý rằng trong một ví dụ phức tạp hơn, sẽ có rất nhiều dòng trong theo dõi ngăn xếp NPE. Nhưng bạn có thể chắc chắn rằng dòng thứ hai (dòng "at" đầu tiên) sẽ cho bạn biết NPE được ném 1 ở đâu .

Nói tóm lại, theo dõi ngăn xếp sẽ cho chúng ta biết rõ ràng tuyên bố nào của chương trình đã ném NPE.

1 - Không hoàn toàn đúng. Có những thứ gọi là ngoại lệ lồng nhau ...

Câu hỏi: Làm cách nào để theo dõi nguyên nhân của ngoại lệ NPE trong mã của tôi?

Đây là phần cứng. Câu trả lời ngắn gọn là áp dụng suy luận logic vào bằng chứng được cung cấp bởi dấu vết ngăn xếp, mã nguồn và tài liệu API có liên quan.

Trước tiên, hãy minh họa bằng ví dụ đơn giản (ở trên). Chúng tôi bắt đầu bằng cách nhìn vào dòng mà dấu vết ngăn xếp đã nói với chúng tôi là nơi NPE xảy ra:

int length = foo.length(); // HERE

Làm thế nào mà có thể ném một NPE?

Trên thực tế, chỉ có một cách: nó chỉ có thể xảy ra nếu foocó giá trị null. Sau đó chúng tôi cố gắng chạy length()phương thức trênnull và ... BANG!

Nhưng (tôi nghe bạn nói) nếu NPE bị ném bên trong lệnh length()gọi thì sao?

Chà, nếu điều đó xảy ra, dấu vết ngăn xếp sẽ khác. Dòng "at" đầu tiên sẽ nói rằng ngoại lệ được ném vào một số dòng trong java.lang.Stringlớp và dòng 4 Test.javasẽ là dòng "at" thứ hai.

Vậy nó nullđến từ đâu? Trong trường hợp này, điều đó là hiển nhiên, và rõ ràng chúng ta cần phải làm gì để khắc phục nó. (Gán một giá trị khác null cho foo.)

OK, vậy chúng ta hãy thử một ví dụ phức tạp hơn một chút. Điều này sẽ yêu cầu một số suy luận hợp lý .

public class Test {

    private static String[] foo = new String[2];

    private static int test(String[] bar, int pos) {
        return bar[pos].length();
    }

    public static void main(String[] args) {
        int length = test(foo, 1);
    }
}

$ javac Test.java 
$ java Test
Exception in thread "main" java.lang.NullPointerException
    at Test.test(Test.java:6)
    at Test.main(Test.java:10)
$ 

Vì vậy, bây giờ chúng tôi có hai dòng "tại". Cái đầu tiên là cho dòng này:

return args[pos].length();

và cái thứ hai là cho dòng này:

int length = test(foo, 1);

Nhìn vào dòng đầu tiên, làm thế nào mà có thể ném NPE? Có hai cách:

  • Nếu giá trị barnullthì bar[pos]sẽ ném NPE.
  • Nếu giá trị bar[pos]nullsau đó gọi length()nó sẽ ném NPE.

Tiếp theo, chúng ta cần tìm ra kịch bản nào giải thích những gì đang thực sự xảy ra. Chúng ta sẽ bắt đầu bằng cách khám phá cái đầu tiên:

Nơi nào barđến từ đâu? Nó là một tham số cho lệnh testgọi phương thức và nếu chúng ta nhìn vào cách testgọi, chúng ta có thể thấy rằng nó đến từ foobiến tĩnh. Ngoài ra, chúng ta có thể thấy rõ rằng chúng ta đã khởi tạo foomột giá trị khác không. Điều đó là đủ để tạm thời bỏ qua lời giải thích này. (Về lý thuyết, một cái gì đó khác có thể thay đổi foo thành null... nhưng điều đó không xảy ra ở đây.)

Vậy còn kịch bản thứ hai của chúng ta thì sao? Vâng, chúng ta có thể thấy đó pos1, vì vậy điều đó có nghĩa là foo[1]phải null. Điều này có thể không?

Quả thực là như vậy! Và đó là vấn đề. Khi chúng tôi khởi tạo như thế này:

private static String[] foo = new String[2];

chúng tôi phân bổ một String[]với hai yếu tố được khởi tạonull . Sau đó, chúng tôi đã không thay đổi nội dung của foo... vì vậy foo[1]vẫn sẽ như vậy null.


425

Giống như bạn đang cố gắng truy cập một đối tượng null. Xem xét ví dụ dưới đây:

TypeA objA;

Tại thời điểm này, bạn vừa khai báo đối tượng này nhưng không được khởi tạo hoặc khởi tạo . Và bất cứ khi nào bạn cố gắng truy cập bất kỳ tài sản hoặc phương thức nào trong đó, nó sẽ ném NullPointerExceptioncó ý nghĩa.

Xem ví dụ dưới đây là tốt:

String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown

1
Nếu chúng ta cho System.out.println (a.length ()); // NullPulumException sẽ bị ném, để bỏ qua điều này, chúng ta có thể xử lý với khối bắt thử. cảm ơn bạn
Vijaya Varma Lanke

359

Một ngoại lệ con trỏ null được đưa ra khi một ứng dụng cố gắng sử dụng null trong trường hợp bắt buộc phải có một đối tượng. Bao gồm các:

  1. Gọi phương thức thể hiện của một nullđối tượng.
  2. Truy cập hoặc sửa đổi trường của một nullđối tượng.
  3. Lấy chiều dài nullnhư thể nó là một mảng.
  4. Truy cập hoặc sửa đổi các vị trí nullnhư thể nó là một mảng.
  5. Ném nullnhư thể nó là một giá trị Ném được.

Các ứng dụng nên ném các thể hiện của lớp này để chỉ ra các mục đích sử dụng bất hợp pháp khác của nullđối tượng.

Tham khảo: http://docs.oracle.com/javase/8/docs/api/java/lang/NullPulumException.html


12
Giữ cho nó đơn giản, tôi thích câu trả lời này, thêm câu này nếu bạn cho là đúng - Truy cập vào thuộc tính chưa được khởi tạo của một đối tượng
Emiliano

5
@Emiliano - chỉ cần truy cập một thuộc tính khởi tạo không gây ra NPE. Đó là những gì bạn >> làm << với giá trị thuộc tính chưa được khởi tạo gây ra NPE.
Stephen C

1
Nếu bạn muốn có nhiều trường hợp hơn: 1) sử dụng a nulllàm mục tiêu của một synchronizedkhối, 2) sử dụng a nulllàm mục tiêu của a switchvà bỏ hộp null.
Stephen C

333

Một nullcon trỏ là một con trỏ đến hư không. Khi bạn hủy đăng ký một con trỏ p, bạn nói "đưa cho tôi dữ liệu tại vị trí được lưu trữ trong" p ". Khi nào plà một nullcon trỏ, vị trí được lưu trữ pnowhere, bạn đang nói" đưa cho tôi dữ liệu tại vị trí 'không ở đâu' ". Rõ ràng, nó không thể làm điều này, vì vậy nó ném a null pointer exception.

Nói chung, đó là vì một cái gì đó đã không được khởi tạo đúng cách.


2
Chúng ta đang tạo một cơ sở dữ liệu? -> NULLđược viết như nulltrong java. Và đó là một điều nhạy cảm trường hợp.
bvdb

3
"Một con trỏ NULL là một con trỏ chỉ vào hư không" Tôi không đồng ý. Con trỏ rỗng không trỏ đến hư không, chúng trỏ đến giá trị null.
TheRealChx101

2
@ TheRealChx101 Một con trỏ null và một con trỏ tới giá trị null là những thứ khác nhau - một con trỏ null không trỏ đến một giá trị null. Giả sử bạn có một con trỏ tới một con trỏ: con trỏ A trỏ đến con trỏ B và con trỏ B là null. Trong trường hợp này, con trỏ A trỏ đến một giá trị null và con trỏ B là một con trỏ null.
MrZebra

321

Rất nhiều lời giải thích đã được trình bày để giải thích nó xảy ra như thế nào và cách khắc phục nó, nhưng bạn cũng nên tuân theo các thực tiễn tốt nhất để tránh NullPointerExceptions.

Xem thêm: Một danh sách tốt các thực hành tốt nhất

Tôi sẽ thêm, rất quan trọng, sử dụng tốt công cụ finalsửa đổi. Sử dụng công cụ sửa đổi "cuối cùng" bất cứ khi nào có thể áp dụng trong Java

Tóm lược:

  1. Sử dụng công cụ finalsửa đổi để thực thi khởi tạo tốt.
  2. Tránh trả về null trong các phương thức, ví dụ trả về các bộ sưu tập trống khi áp dụng.
  3. Sử dụng chú thích @NotNull@Nullable
  4. Thất bại nhanh và sử dụng các xác nhận để tránh lan truyền các đối tượng null qua toàn bộ ứng dụng khi chúng không nên rỗng.
  5. Sử dụng bằng với một đối tượng đã biết trước: if("knownObject".equals(unknownObject)
  6. Thích valueOf()hơn toString().
  7. Sử dụng StringUtilsphương pháp an toàn null StringUtils.isEmpty(null).
  8. Sử dụng Java 8 Tùy chọn làm giá trị trả về trong các phương thức, lớp Tùy chọn cung cấp giải pháp đại diện cho các giá trị tùy chọn thay vì tham chiếu null.

4
Trong các dự án j2ee, ngoại lệ Nullpulum rất phổ biến. Một số trường hợp biến tham chiếu có giá trị null. Vì vậy, bạn nên kiểm tra khởi tạo biến đúng. Và trong câu lệnh có điều kiện, bạn phải luôn kiểm tra xem cờ hoặc tham chiếu có chứa null hay không như: - if (flag! = 0) {mã ur sử dụng cờ}
Amaresh Pattanayak

14
Điều đáng nói là một số IDE (ví dụ Eclipse) cung cấp các phân tích vô hiệu hóa tự động dựa trên các chú thích có thể tùy chỉnh (ví dụ @Nullablenhư được liệt kê ở trên) và cảnh báo về các lỗi tiềm ẩn. Cũng có thể suy ra và tạo các chú thích như vậy (ví dụ IntelliJ có thể làm điều đó) dựa trên cấu trúc mã hiện có.
Jan Chimiak

4
Điều đầu tiên nên làm là trước khi sử dụng một đối tượng nullable, bạn nên kiểm tra xem nó có null không, bằng cách sử dụng if (obj==null). Nếu nó là null thì bạn cũng nên viết mã để xử lý điều đó.
Lakmal Vithanage

4
IMO, tốt nhất là tránh trả về các đối tượng null trong các phương thức khi có thể và sử dụng chú thích khi các tham số đầu vào null không được phép để, theo hợp đồng, giảm số lượng êifif (obj == null) ọ trong mã và cải thiện mã dễ đọc.
LG

4
Đọc điều này ... trước khi bạn chấp nhận những "thực tiễn tốt nhất" này là sự thật : isfice.com/blog/archives/27
Stephen C

316

Trong Java, mọi thứ (không bao gồm các kiểu nguyên thủy) đều ở dạng một lớp.

Nếu bạn muốn sử dụng bất kỳ đối tượng nào thì bạn có hai giai đoạn:

  1. Khai báo
  2. Khởi tạo

Thí dụ:

  • Tờ khai: Object object;
  • Khởi tạo: object = new Object();

Tương tự cho khái niệm mảng:

  • Tờ khai: Item item[] = new Item[5];
  • Khởi tạo: item[0] = new Item();

Nếu bạn không đưa ra phần khởi tạo thì NullPointerExceptionphát sinh.


3
Một NullPulumException thường xảy ra khi gọi phương thức của một cá thể. Ví dụ, nếu bạn khai báo một tham chiếu nhưng không làm cho nó trỏ đến bất kỳ trường hợp nào, NullPulumException sẽ xảy ra khi bạn gọi phương thức của nó. chẳng hạn như: YourClass ref = null; // hoặc ref = otherRef; // nhưng AnotherRef chưa chỉ ra bất kỳ trường hợp ref.someMethod (); // nó sẽ ném NullPulumException. Thường sửa nó theo cách này: Trước khi phương thức được gọi, xác định xem tham chiếu có null không. chẳng hạn như: if (yourRef! = null) {yourRef.someMethod (); }
sunhang

2
Hoặc sử dụng chụp ngoại lệ: chẳng hạn như: thử {yourRef.someMethod (); } Catch (NullPulumException e) {// TODO}
sunhang


315

Một ngoại lệ con trỏ null là một chỉ báo cho thấy bạn đang sử dụng một đối tượng mà không khởi tạo nó.

Ví dụ, bên dưới là một lớp sinh viên sẽ sử dụng nó trong mã của chúng tôi.

public class Student {

    private int id;

    public int getId() {
        return this.id;
    }

    public setId(int newId) {
        this.id = newId;
    }
}

Đoạn mã dưới đây cung cấp cho bạn một ngoại lệ con trỏ null.

public class School {

    Student student;

    public School() {
        try {
            student.getId();
        }
        catch(Exception e) {
            System.out.println("Null pointer exception");
        }
    }
}

Bởi vì bạn đang sử dụng student, nhưng bạn đã quên khởi tạo nó như trong mã chính xác được hiển thị bên dưới:

public class School {

    Student student;

    public School() {
        try {
            student = new Student();
            student.setId(12);
            student.getId();
        }
        catch(Exception e) {
            System.out.println("Null pointer exception");
        }
    }
}

7
Mặc dù đây là một ví dụ hay, tôi có thể hỏi những gì nó thêm vào câu hỏi chưa được bao gồm trong tất cả các câu trả lời khác không?
Bí ẩn

13
Đơn giản là không phù hợp để sử dụng từ "chưa được khởi tạo" ở đây. Ví dụ bạn đã hiển thị trên thực tế là "khởi tạo" và nó được khởi tạo bằng null. Đối với các biến chưa được khởi tạo, trình biên dịch sẽ khiếu nại với bạn.
Adrian Shum

2
Một NPE có thể là một chỉ báo cho thấy bạn đang sử dụng một trường chưa được khởi tạo. Nó có thể là một chỉ báo rằng bạn đang làm những việc khác. Việc đơn giản hóa một nguyên nhân duy nhất như thế này không giúp ai đó giải quyết các vấn đề về NPE ... nếu nguyên nhân thực sự không phải là nguyên nhân này.
Stephen C

309

Trong Java, tất cả các biến bạn khai báo thực sự là "tham chiếu" đến các đối tượng (hoặc nguyên thủy) chứ không phải chính các đối tượng.

Khi bạn cố gắng thực hiện một phương thức đối tượng, tham chiếu sẽ yêu cầu đối tượng sống thực hiện phương thức đó. Nhưng nếu tham chiếu đang tham chiếu NULL (nothing, zero, void, nada) thì không có cách nào phương thức được thực thi. Sau đó, thời gian chạy cho bạn biết điều này bằng cách ném NullPulumException.

Tham chiếu của bạn là "trỏ" đến null, do đó "Null -> Con trỏ".

Đối tượng sống trong không gian bộ nhớ VM và cách duy nhất để truy cập nó là sử dụng các thistham chiếu. Lấy ví dụ này:

public class Some {
    private int id;
    public int getId(){
        return this.id;
    }
    public setId( int newId ) {
        this.id = newId;
    }
}

Và ở một nơi khác trong mã của bạn:

Some reference = new Some();    // Point to a new object of type Some()
Some otherReference = null;     // Initiallly this points to NULL

reference.setId( 1 );           // Execute setId method, now private var id is 1

System.out.println( reference.getId() ); // Prints 1 to the console

otherReference = reference      // Now they both point to the only object.

reference = null;               // "reference" now point to null.

// But "otherReference" still point to the "real" object so this print 1 too...
System.out.println( otherReference.getId() );

// Guess what will happen
System.out.println( reference.getId() ); // :S Throws NullPointerException because "reference" is pointing to NULL remember...

Đây là một điều quan trọng cần biết - khi không còn tham chiếu đến một đối tượng (trong ví dụ ở trên khi referenceotherReferencecả hai đều trỏ đến null) thì đối tượng đó là "không thể truy cập được". Không có cách nào chúng ta có thể làm việc với nó, vì vậy đối tượng này đã sẵn sàng để được thu gom rác và đến một lúc nào đó, VM sẽ giải phóng bộ nhớ được sử dụng bởi đối tượng này và sẽ phân bổ một đối tượng khác.


280

Một lần xuất hiện khác xảy NullPointerExceptionra khi người ta khai báo một mảng đối tượng, sau đó ngay lập tức cố gắng để hủy bỏ các yếu tố bên trong nó.

String[] phrases = new String[10];
String keyPhrase = "Bird";
for(String phrase : phrases) {
    System.out.println(phrase.equals(keyPhrase));
}

NPE cụ thể này có thể tránh được nếu thứ tự so sánh bị đảo ngược; cụ thể là sử dụng .equalstrên một đối tượng không null được bảo đảm.

Tất cả các phần tử bên trong một mảng được khởi tạo thành giá trị ban đầu chung của chúng ; đối với bất kỳ loại mảng đối tượng, điều đó có nghĩa là tất cả các phần tử là null.

Bạn phải khởi tạo các thành phần trong mảng trước khi truy cập hoặc hủy bỏ chúng.

String[] phrases = new String[] {"The bird", "A bird", "My bird", "Bird"};
String keyPhrase = "Bird";
for(String phrase : phrases) {
    System.out.println(phrase.equals(keyPhrase));
}

hoạt động trên đối tượng chưa được khởi tạo ở cấp thể hiện (không phải cấp độ lớp) sẽ dẫn đến NullPulumException. hoạt động cần phải được cụ thể. nếu hoạt động ở cấp độ lớp, nói rằng gọi một phương thức tĩnh trên đối tượng chưa được khởi tạo thì nó sẽ không ném ngoại lệ NullPulumException. Ngay cả các đối tượng lớp trình bao bọc nguyên thủy cũng ném NullPulumException.
Shailendra Singh

1. NullPulumException là RuntimeException, điều đó có nghĩa là sẽ xuất hiện khi chương trình của bạn đang chạy, bạn sẽ không ở thời gian biên dịch.! :(, nhưng hầu hết các IDE giúp bạn khám phá điều này. 2. Tối thiểu hóa việc sử dụng từ khóa 'null' trong các câu lệnh gán. :) Url tham chiếu:
tomj0101

@ tomj0101 Tôi hoàn toàn không rõ lý do tại sao bạn đưa ra nhận xét đó ... Nhưng đến điểm thứ hai của bạn, một mô hình trước đó Optionallà trả về null. Các từ khóa là tốt. Biết làm thế nào để bảo vệ chống lại nó là rất quan trọng. Điều này cung cấp một sự xuất hiện phổ biến của nó và cách để giảm thiểu nó.
Makoto

NullPulumException là một ngoại lệ trong thời gian chạy không được khuyến nghị để bắt nó, nhưng thay vào đó nên tránh nó.
Shomu

2
@Shomu: Tại thời điểm nào tôi thậm chí đề nghị rằng nó nên được bắt?
Makoto
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.