Che cos'è una NullPointerException e come si risolve?

Oct 20 2008

Cosa sono le eccezioni puntatore nullo ( java.lang.NullPointerException) e cosa le causa?

Quali metodi / strumenti possono essere utilizzati per determinare la causa in modo da impedire all'eccezione di causare la chiusura anticipata del programma?

Risposte

3848 VincentRamdhanie Oct 20 2008 at 20:54

Quando dichiari una variabile di riferimento (cioè un oggetto) stai realmente creando un puntatore a un oggetto. Considera il codice seguente in cui dichiari una variabile di tipo primitivo int:

int x;
x = 10;

In questo esempio, la variabile xè an inte Java la inizializzerà 0per te. Quando gli si assegna il valore di 10sulla seconda riga, il valore di 10viene scritto nella posizione di memoria a cui fa riferimento x.

Ma, quando provi a dichiarare un tipo di riferimento , accade qualcosa di diverso. Prendi il codice seguente:

Integer num;
num = new Integer(10);

La prima riga dichiara una variabile denominata num, ma in realtà non contiene ancora un valore primitivo. Invece, contiene un puntatore (perché il tipo è Integerche è un tipo di riferimento). Dal momento che non hai ancora detto a cosa puntare, Java lo imposta null, il che significa " Non sto indicando nulla ".

Nella seconda riga, la newparola chiave viene utilizzata per istanziare (o creare) un oggetto di tipo Integere la variabile del puntatore numviene assegnata a Integerquell'oggetto.

La NullPointerExceptionverifica quando si dichiara una variabile, ma non ha creato un oggetto e assegnare alla variabile prima di tentare di utilizzare i contenuti della variabile (denominata dereferenziazione ). Quindi stai indicando qualcosa che in realtà non esiste.

La dereferenziazione di solito si verifica quando si utilizza .per accedere a un metodo o campo, oppure si utilizza [per indicizzare un array.

Se si tenta di dereferenziare numPRIMA di creare l'oggetto, si ottiene un file NullPointerException. Nei casi più banali, il compilatore rileverà il problema e ti informerà che " num may not have been initialized," ma a volte potresti scrivere codice che non crea direttamente l'oggetto.

Ad esempio, potresti avere un metodo come segue:

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

In tal caso, non si crea l'oggetto obj, ma si presume che sia stato creato prima della doSomething()chiamata del metodo. Nota, è possibile chiamare il metodo in questo modo:

doSomething(null);

In tal caso, objis null, e l'istruzione obj.myMethod()genererà un file NullPointerException.

Se il metodo ha lo scopo di fare qualcosa all'oggetto passato come fa il metodo precedente, è appropriato lanciare il NullPointerExceptionperché è un errore del programmatore e il programmatore avrà bisogno di queste informazioni per scopi di debug.

Oltre ai messaggi NullPointerExceptiongenerati come risultato della logica del metodo, puoi anche controllare gli argomenti del metodo per i nullvalori e lanciare esplicitamente NPE aggiungendo qualcosa di simile all'inizio di un metodo:

//Throws an NPE with a custom error message if obj is null
Objects.requireNonNull(obj, "obj must not be null");

Nota che è utile dire chiaramente nel tuo messaggio di errore quale oggetto non può essere null. Il vantaggio di eseguire una convalida in questo modo è che 1) puoi restituire i tuoi messaggi di errore più chiari e 2) per il resto del metodo sai che a meno che non objvenga riassegnato, non è nullo e può essere dereferenziato in modo sicuro.

In alternativa, potrebbero esserci casi in cui lo scopo del metodo non è esclusivamente quello di operare sull'oggetto passato e quindi un parametro nullo può essere accettabile. In questo caso, dovresti verificare la presenza di un parametro nullo e comportarti in modo diverso. Dovresti anche spiegarlo nella documentazione. Ad esempio, doSomething()potrebbe essere scritto come:

/**
  * @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
    }
}

Infine, come individuare l'eccezione e la causa utilizzando Stack Trace

Quali metodi / strumenti possono essere utilizzati per determinare la causa in modo da impedire all'eccezione di causare la chiusura anticipata del programma?

Il sonar con bug di ricerca può rilevare NPE. Il sonar può rilevare eccezioni di puntatore nullo causate da JVM dinamicamente

Ora Java 14 ha aggiunto una nuova funzionalità del linguaggio per mostrare la causa principale di NullPointerException. Questa funzionalità del linguaggio fa parte della JVM commerciale di SAP dal 2006. Quanto segue è una lettura di 2 minuti per comprendere questa straordinaria funzionalità del linguaggio.

https://jfeatures.com/blog/NullPointerException

In java 14, di seguito è riportato un messaggio di eccezione NullPointerException di esempio:

nel thread "main" java.lang.NullPointerException: Impossibile richiamare "java.util.List.size ()" perché "list" è nullo

898 BilltheLizard Oct 20 2008 at 20:20

NullPointerExceptionLe eccezioni che si verificano quando si tenta di utilizzare un riferimento che non punta a nessuna posizione in memoria (null) come se stesse facendo riferimento a un oggetto. La chiamata a un metodo su un riferimento null o il tentativo di accedere a un campo di un riferimento null attiverà un NullPointerException. Questi sono i più comuni, ma altri modi sono elencati nella NullPointerExceptionpagina javadoc.

Probabilmente il codice di esempio più veloce che potrei inventare per illustrare un NullPointerExceptionsarebbe:

public class Example {

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

}

Nella prima riga interna main, imposto esplicitamente il Objectriferimento objuguale a null. Ciò significa che ho un riferimento, ma non punta a nessun oggetto. Dopodiché, provo a trattare il riferimento come se puntasse a un oggetto chiamando un metodo su di esso. Ciò si traduce in un NullPointerExceptionperché non è presente alcun codice da eseguire nella posizione a cui punta il riferimento.

(Questo è un tecnicismo, ma penso che valga la pena menzionarlo: un riferimento che punta a null non è lo stesso di un puntatore C che punta a una posizione di memoria non valida. Un puntatore null non punta letteralmente da nessuna parte , il che è leggermente diverso da che punta a una posizione che sembra non essere valida.)

709 fgb Jun 08 2014 at 02:22

Cos'è una NullPointerException?

Un buon punto di partenza è JavaDocs . Hanno questo coperto:

Generato quando un'applicazione tenta di utilizzare null in un caso in cui è richiesto un oggetto. Questi includono:

  • Chiamata al metodo di istanza di un oggetto null.
  • Accesso o modifica del campo di un oggetto nullo.
  • Prendendo la lunghezza di null come se fosse un array.
  • Accedere o modificare gli slot di null come se fosse un array.
  • Lanciare null come se fosse un valore Throwable.

Le applicazioni dovrebbero lanciare istanze di questa classe per indicare altri usi illegali dell'oggetto null.

È anche il caso che se si tenta di utilizzare un riferimento nullo con synchronized, verrà generata anche questa eccezione, per JLS :

SynchronizedStatement:
    synchronized ( Expression ) Block
  • In caso contrario, se il valore di Expression è null, NullPointerExceptionviene generato a.

Come lo aggiusto?

Quindi hai un file NullPointerException. Come lo aggiusti? Facciamo un semplice esempio che genera un 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();
    }
}

Identifica i valori nulli

Il primo passo è identificare esattamente quali valori stanno causando l'eccezione . Per questo, dobbiamo fare un po 'di debug. È importante imparare a leggere uno stacktrace . Questo ti mostrerà dove è stata generata l'eccezione:

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)

Qui, vediamo che l'eccezione viene lanciata sulla riga 13 (nel printStringmetodo). Guarda la riga e controlla quali valori sono nulli aggiungendo istruzioni di registrazione o utilizzando un debugger . Scopriamo che sè nullo e la chiamata al lengthmetodo su di esso genera l'eccezione. Possiamo vedere che il programma smette di lanciare l'eccezione quando s.length()viene rimosso dal metodo.

Traccia da dove provengono questi valori

Quindi controlla da dove proviene questo valore. Seguendo i chiamanti del metodo, vediamo che sviene passato con printString(name)nel print()metodo ed this.nameè nullo.

Traccia dove dovrebbero essere impostati questi valori

Dov'è this.nameambientato? Nel setName(String)metodo. Con un po 'più di debug, possiamo vedere che questo metodo non viene chiamato affatto. Se il metodo è stato chiamato, assicurati di controllare l' ordine in cui vengono chiamati questi metodi e il metodo set non viene chiamato dopo il metodo print.

Questo è sufficiente per darci una soluzione: aggiungi una chiamata a printer.setName()prima di chiamare printer.print().

Altre correzioni

La variabile può avere un valore predefinito (e setNamepuò impedire che venga impostata su null):

private String name = "";

Il metodo printo printStringpuò verificare la presenza di null , ad esempio:

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

Oppure puoi progettare la classe in modo che name abbia sempre un valore non nullo :

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();
    }
}

Guarda anche:

  • Evitare le istruzioni "! = Null" in Java?

Non riesco ancora a trovare il problema

Se hai provato a eseguire il debug del problema e non hai ancora una soluzione, puoi pubblicare una domanda per ulteriore assistenza, ma assicurati di includere ciò che hai provato finora. Come minimo, includi lo stacktrace nella domanda e contrassegna i numeri di riga importanti nel codice. Inoltre, prova prima a semplificare il codice (vedi SSCCE ).

516 StephenC Jun 22 2014 at 09:16

Domanda: cosa causa un NullPointerException(NPE)?

Come dovreste sapere, tipi Java sono divisi in tipi primitivi ( boolean, int, ecc) e tipi di riferimento . I tipi di riferimento in Java consentono di utilizzare il valore speciale nullche è il modo Java di dire "nessun oggetto".

A NullPointerExceptionviene lanciato in fase di esecuzione ogni volta che il programma tenta di utilizzare a nullcome se fosse un riferimento reale. Ad esempio, se scrivi questo:

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

l'istruzione etichettata "HERE" tenterà di eseguire il length()metodo su un nullriferimento e questo genererà un file NullPointerException.

Ci sono molti modi in cui puoi usare un nullvalore che risulterà in un file NullPointerException. In effetti, le uniche cose che puoi fare con un nullsenza causare un NPE sono:

  • assegnarlo a una variabile di riferimento o leggerlo da una variabile di riferimento,
  • assegnarlo a un elemento dell'array o leggerlo da un elemento dell'array (a condizione che il riferimento all'array stesso non sia nullo!),
  • passarlo come parametro o restituirlo come risultato, o
  • testarlo utilizzando gli ==o !=operatori, o instanceof.

Domanda: come leggo lo stacktrace NPE?

Supponiamo che io compili ed esegua il programma sopra:

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

Prima osservazione: la compilazione riesce! Il problema nel programma NON è un errore di compilazione. È un errore di runtime . (Alcuni IDE potrebbero avvertire che il tuo programma genererà sempre un'eccezione ... ma il javaccompilatore standard no.)

Seconda osservazione: quando eseguo il programma, emette due righe di "gobbledy-gook". SBAGLIATO!! Non è gobbledy-gook. È uno stacktrace ... e fornisce informazioni vitali che ti aiuteranno a rintracciare l'errore nel tuo codice se ti prendi il tempo di leggerlo attentamente.

Quindi diamo un'occhiata a cosa dice:

Exception in thread "main" java.lang.NullPointerException

La prima riga della traccia dello stack ti dice una serie di cose:

  • Ti dice il nome del thread Java in cui è stata generata l'eccezione. Per un programma semplice con un thread (come questo), sarà "main". Andiamo avanti ...
  • Ti dice il nome completo dell'eccezione che è stata lanciata; cioè java.lang.NullPointerException.
  • Se l'eccezione ha un messaggio di errore associato, verrà emesso dopo il nome dell'eccezione. NullPointerExceptionè insolito sotto questo aspetto, perché raramente ha un messaggio di errore.

La seconda riga è la più importante nella diagnosi di un NPE.

at Test.main(Test.java:4)

Questo ci dice una serie di cose:

  • "at Test.main" dice che eravamo nel mainmetodo della Testclasse.
  • "Test.java:4" fornisce il nome del file sorgente della classe, E ci dice che l'istruzione in cui si è verificato si trova nella riga 4 del file.

Se conti le righe nel file sopra, la riga 4 è quella che ho etichettato con il commento "QUI".

Si noti che in un esempio più complicato, ci saranno molte righe nella traccia dello stack NPE. Ma puoi essere certo che la seconda riga (la prima riga "at") ti dirà dove è stato lanciato l'NPE 1 .

In breve, la traccia dello stack ci dirà in modo inequivocabile quale affermazione del programma ha generato l'NPE.

1 - Non proprio vero. Ci sono cose chiamate eccezioni annidate ...

Domanda: come faccio a rintracciare la causa dell'eccezione NPE nel mio codice?

Questa è la parte difficile. La risposta breve è applicare l'inferenza logica all'evidenza fornita dalla traccia dello stack, dal codice sorgente e dalla documentazione API pertinente.

Illustriamo prima con il semplice esempio (sopra). Iniziamo osservando la riga che la traccia dello stack ci ha detto è dove si è verificato l'NPE:

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

Come può generare un NPE?

In effetti, c'è un solo modo: può accadere solo se fooha il valore null. Proviamo quindi a eseguire il length()metodo nulle ... BANG!

Ma (ti sento dire) cosa succederebbe se l'NPE fosse stato lanciato all'interno della length()chiamata al metodo?

Bene, se ciò accadesse, la traccia dello stack apparirebbe diversa. La prima riga "at" direbbe che l'eccezione è stata lanciata in una riga della java.lang.Stringclasse e la riga 4 di Test.javasarebbe la seconda riga "at".

Allora da dove nullviene? In questo caso, è ovvio ed è ovvio cosa dobbiamo fare per risolverlo. (Assegna un valore non nullo a foo.)

OK, quindi proviamo un esempio leggermente più complicato. Ciò richiederà una deduzione logica .

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) $ 

Quindi ora abbiamo due righe "at". Il primo è per questa linea:

return args[pos].length();

e il secondo è per questa linea:

int length = test(foo, 1);

Guardando la prima riga, come potrebbe generare un NPE? Ci sono due modi:

  • Se il valore di barè nullquindi bar[pos]lancerà un NPE.
  • Se il valore di bar[pos]è nullquindi invitante length(), genererà un NPE.

Successivamente, dobbiamo capire quale di questi scenari spiega cosa sta realmente accadendo. Inizieremo esplorando il primo:

Da dove barviene? È un parametro per la testchiamata al metodo e se guardiamo come è teststato chiamato, possiamo vedere che proviene dalla foovariabile statica. Inoltre, possiamo vedere chiaramente che abbiamo inizializzato foosu un valore non nullo. Ciò è sufficiente per respingere provvisoriamente questa spiegazione. (In teoria, qualcos'altro potrebbe cambiare foo in null... ma questo non sta accadendo qui.)

E il nostro secondo scenario? Bene, possiamo vedere che posè 1, quindi significa che foo[1]deve essere null. È possibile?

Certo che lo è! E questo è il problema. Quando inizializziamo in questo modo:

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

assegniamo a String[]con due elementi che vengono inizializzatinull . Dopodiché, non abbiamo cambiato il contenuto di foo... così foo[1]sarà ancora null.

432 RakeshBurbure Oct 20 2008 at 20:21

È come se stessi tentando di accedere a un oggetto che è null. Considera l'esempio seguente:

TypeA objA;

A questo punto hai appena dichiarato questo oggetto ma non è stato inizializzato o istanziato . E ogni volta che provi ad accedere a qualsiasi proprietà o metodo al suo interno, verrà visualizzato il messaggio NullPointerExceptionche ha senso.

Vedi anche questo esempio di seguito:

String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
365 nathan1138 Apr 17 2013 at 09:57

Un'eccezione del puntatore null viene generata quando un'applicazione tenta di utilizzare null in un caso in cui è richiesto un oggetto. Questi includono:

  1. Chiamata al metodo di istanza di un nulloggetto.
  2. Accesso o modifica del campo di un nulloggetto.
  3. Prendendo la lunghezza di nullcome se fosse un array.
  4. Accedere o modificare gli slot di nullcome se fosse un array.
  5. Lanciare nullcome se fosse un valore Lanciabile.

Le applicazioni dovrebbero generare istanze di questa classe per indicare altri usi illegali nulldell'oggetto.

Riferimento: http://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html

341 MrZebra Oct 20 2008 at 20:25

Un nullpuntatore è quello che punta a nulla. Quando si dereferenzia un puntatore p, si dice "dammi i dati nella posizione memorizzata in" p ". Quando pè un nullpuntatore, la posizione memorizzata in pè nowhere, stai dicendo" dammi i dati nella posizione 'da nessuna parte' ". Ovviamente, non può farlo, quindi genera un file null pointer exception.

In generale, è perché qualcosa non è stato inizializzato correttamente.

329 L.G. Jun 25 2014 at 18:17

Sono già presenti molte spiegazioni per spiegare come accade e come risolverlo, ma dovresti anche seguire le migliori pratiche per evitare NullPointerExceptiondel tutto.

Vedi anche: Un buon elenco di best practice

Aggiungerei, molto importante, fare un buon uso del finalmodificatore. Utilizzo del modificatore "finale" ogniqualvolta applicabile in Java

Sommario:

  1. Usa il finalmodificatore per imporre una buona inizializzazione.
  2. Evita di restituire null nei metodi, ad esempio restituendo raccolte vuote quando applicabile.
  3. Usa annotazioni @NotNulle@Nullable
  4. Fallisci velocemente e usa asserzioni per evitare la propagazione di oggetti nulli attraverso l'intera applicazione quando non dovrebbero essere nulli.
  5. Usa prima uguale con un oggetto noto: if("knownObject".equals(unknownObject)
  6. Preferiscono valueOf()sopra toString().
  7. Utilizza StringUtilsmetodi null safe StringUtils.isEmpty(null).
  8. Usa Java 8 Optional come valore di ritorno nei metodi, la classe Optional fornisce una soluzione per rappresentare valori facoltativi invece di riferimenti nulli.
323 ashishbhatt Jan 28 2012 at 13:45

In Java, tutto (esclusi i tipi primitivi) ha la forma di una classe.

Se vuoi usare qualsiasi oggetto, hai due fasi:

  1. Dichiarare
  2. Inizializzazione

Esempio:

  • Dichiarazione: Object object;
  • Inizializzazione: object = new Object();

Lo stesso per il concetto di array:

  • Dichiarazione: Item item[] = new Item[5];
  • Inizializzazione: item[0] = new Item();

Se non stai dando la sezione di inizializzazione, allora il NullPointerExceptionsorgere.

322 javidpiprani Sep 24 2013 at 13:01

Un'eccezione del puntatore nullo è un indicatore che stai utilizzando un oggetto senza inizializzarlo.

Ad esempio, di seguito è riportata una classe di studenti che la utilizzerà nel nostro codice.

public class Student {

    private int id;

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

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

Il codice seguente ti dà un'eccezione del puntatore nullo.

public class School {

    Student student;

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

Perché stai usando student, ma hai dimenticato di inizializzarlo come nel codice corretto mostrato di seguito:

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");
        }
    }
}
315 OscarRyz Oct 21 2008 at 03:05

In Java tutte le variabili che dichiari sono in realtà "riferimenti" agli oggetti (o primitive) e non agli oggetti stessi.

Quando si tenta di eseguire un metodo oggetto, il riferimento chiede all'oggetto vivente di eseguire quel metodo. Ma se il riferimento fa riferimento a NULL (niente, zero, void, nada), non è possibile che il metodo venga eseguito. Quindi il runtime ti fa sapere questo lanciando una NullPointerException.

Il tuo riferimento "punta" a null, quindi "Null -> Pointer".

L'oggetto risiede nello spazio di memoria della VM e l'unico modo per accedervi è utilizzare i thisriferimenti. Prendi questo esempio:

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

E in un altro punto del codice:

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...

Questa è una cosa importante da sapere - quando non ci sono più riferimenti a un oggetto (nell'esempio sopra quando referenceed otherReferenceentrambi puntano a null) allora l'oggetto è "irraggiungibile". Non è possibile lavorarci sopra, quindi questo oggetto è pronto per essere sottoposto a garbage collection e, a un certo punto, la VM libererà la memoria utilizzata da questo oggetto e ne assegnerà un altro.

287 Makoto May 25 2014 at 13:11

Un'altra occorrenza di a si NullPointerExceptionverifica quando si dichiara un array di oggetti, quindi si cerca immediatamente di dereferenziare gli elementi al suo interno.

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

Questo particolare NPE può essere evitato invertendo l'ordine di confronto; vale a dire, l'uso .equalssu un oggetto non nullo garantito.

Tutti gli elementi all'interno di un array vengono inizializzati al loro valore iniziale comune ; per qualsiasi tipo di array di oggetti, ciò significa che tutti gli elementi lo sono null.

È necessario inizializzare gli elementi nell'array prima di accedervi o dereferenziarli.

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