Che cos'è un NullPointerException e come faccio a risolvere il problema?

Quali sono le eccezioni Null Pointer ( java.lang.NullPointerException ) e cosa li provoca?

Quali methods / strumenti possono essere utilizzati per determinare la causa in modo da arrestare l'exception da causare il fine del programma prematuro?

Solutions Collecting From Web of "Che cos'è un NullPointerException e come faccio a risolvere il problema?"

Quando si dichiara una variabile di riferimento (cioè un object) stai realmente creando un puntatore a un object. Si consideri il seguente codice in cui si dichiara una variabile di tipo primitivo int :

 int x; x = 10; 

In questo esempio la variabile x è un int e Java lo inizierà a 0 per te. Quando lo assegni a 10 nella seconda row il valore 10 viene scritto nella posizione di memory indicata da x.

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

 Integer num; num = new Integer(10); 

La prima row dichiara una variabile denominata num , ma non contiene un valore primitivo. Invece contiene un puntatore (perché il tipo è Integer che è un tipo di riferimento). Dal momento che non hai ancora detto che cosa puntare a Java, la imposta nulla, cioè "Non sto indicando niente".

Nella seconda row, la new parola chiave viene utilizzata per istanziare (o creare) un object di tipo Integer e la variabile puntatore num è assegnata a questo object. Ora è ansible fare riferimento all'object utilizzando l'operatore di dereferencing . (un punto).

L' Exception che hai chiesto riguarda quando si dichiara una variabile ma non ha creato un object. Se si tenta di dereference num PRIMA di creare l'object si ottiene un NullPointerException . Nei casi più banali il compilatore prenderà il problema e ti farà sapere che "num non è stato inizializzato" ma a volte scrivi un codice che non crea direttamente l'object.

Ad esempio, puoi avere un metodo come segue:

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

nel qual caso non stai creando l'object obj , piuttosto supponendo che sia stato creato prima che venisse chiamato il metodo doSomething . Purtroppo è ansible call il metodo come questo:

 doSomething(null); 

nel qual caso obj è nullo. Se il metodo intende fare qualcosa all'object passato, è opportuno lanciare NullPointerException perché è un errore del programmatore e il programmatore avrà bisogno di tali informazioni per scopi di debug.

In alternativa, ci possono essere casi in cui lo scopo del metodo non è solo di operare sul passato in object e quindi un parametro null può essere accettabile. In questo caso, è necessario verificare un parametro null e comportrsi in modo diverso. Dovresti anche spiegare questo 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 la posizione dell'exception e causare l'utilizzo della traccia Stack

NullPointerException s sono eccezioni che si verificano quando si tenta di utilizzare un riferimento che indica nessuna posizione in memory (null) come se si riferisse ad un object. Chiamare un metodo su un riferimento null o tentare di accedere a un field di un riferimento null innescherà un NullPointerException . Questi sono i più comuni, ma altri methods sono elencati nella pagina NullPointerException .

Probabilmente il codice di esempio più veloce che potrei presentare per illustrare un NullPointerException sarebbe:

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

Nella prima row all'interno di main I'm esplicitamente impostando il riferimento di Object obj uguale a null. Questo significa che ho un riferimento, ma non si riferisce a nessun object. Dopo di che, cerco di trattare il riferimento come se indica un object chiamando un metodo su di esso. Ciò comport un NullPointerException perché non esiste alcun codice da eseguire nella posizione in cui il riferimento si rivolge.

(Questo è un aspetto tecnico, ma penso che sia da ricordare: un riferimento che indica null non è lo stesso di un puntatore C. che indica una posizione di memory non valida. Un puntatore nullo non è letteralmente indicato da nessuna parte , che è sottilmente diverso da indicando una posizione che non sia valida.)

Che cos'è un NullPointerException?

Un buon punto di partenza è il JavaDocs . Hanno questo fatto:

Perdita quando un'applicazione tenta di utilizzare null in un caso in cui è necessario un object. Questi includono:

  • Chiamare il metodo di istanza di un object nullo.
  • Accesso o modifica del field di un object nullo.
  • Prendendo la lunghezza di null come se fosse un arrays.
  • Accesso o modifica delle slot di null come se fosse un arrays.
  • Lanciare null come se fosse un valore Throwable.

Le applicazioni dovrebbero lanciare istanze di questa class per indicare altri usi illeciti dell'object null.

È anche il caso che se si tenta di utilizzare un riferimento null con synchronized , che butterà anche questa exception, per JLS :

 SynchronizedStatement: synchronized ( Expression ) Block 
  • Altrimenti, se il valore dell'espressione è nullo, viene generato un NullPointerException .

Come lo aggiusto?

Quindi hai un NullPointerException , come fai a risolvere il problema? Prendiamo un semplice esempio che lanci 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(); } } 

Identificare i valori nulli

Il primo passo sta identificando esattamente quali valori causano l'exception . Per questo dobbiamo fare un debug. È importnte imparare a leggere una stacktrace . Ciò mostrerà where è stata generata l'exception:

 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'exception viene generata sulla row 13 (nel metodo printString). Guarda la linea e controlla i valori nulli aggiungendo le istruzioni di logging o utilizzando un debugger . Scopriamo che s è nullo, e chiamando il metodo di length su esso getta l'exception. Possiamo vedere che il programma smette di lanciare l'exception quando s.length() viene rimosso dal metodo.

Traccia da where provengono questi valori

Avanti verificare where questo valore viene. Seguendo i chiamanti del metodo, vediamo che s viene passato con printString(name) nel metodo print() e this.name è nullo.

Traccia where questi valori dovrebbero essere impostati

Dove si trova questo nome? Nel metodo setName(String) . Con alcuni debugging, possiamo vedere che questo metodo non viene chiamato affatto. Se il metodo è stato chiamato, assicurarsi di controllare l' ordine che questi methods sono chiamati e il metodo impostato non viene richiamato dopo il metodo di printing.

Ciò è sufficiente per darci una soluzione: aggiungere una chiamata a printer.setName() prima di call printer.print() .

Altre correzioni

La variabile può avere un valore predefinito (e setName può impedire l'impostazione di null):

 private String name = ""; 

Il metodo print or printString può verificare il null , ad esempio:

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

Oppure è ansible progettare la class in modo che il 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 risolvere il problema e ancora non hai una soluzione, puoi submit una domanda per ulteriori informazioni, ma assicuratevi di includere finora quello che hai provato. Ad un minimo, includere la stacktrace nella domanda e contrassegnare i numbers di row importnti nel codice. Inoltre, prova a semplificare prima il codice (vedere SSCCE ).

Domanda: Che cosa provoca NullPointerException ?

Come si deve sapere, i tipi Java sono divisi in tipi primitivi ( boolean , int etc) e tipi di riferimento . I tipi di riferimento in Java consentono di utilizzare il valore speciale null che è il modo Java di dire "nessun object".

Un NullPointerException viene lanciato al runtime each volta che il programma tenta di utilizzare un null come se fosse un vero riferimento. Ad esempio, se si scrive:

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

l'istruzione denominata "QUI" tenterà di eseguire il metodo NullPointerException length() su un riferimento null e questo lancerà un NullPointerException .

Ci sono molti modi in cui è ansible utilizzare un valore null che provocherà un NullPointerException . Se il fatto, le uniche cose che puoi fare con un null senza causare un NPE sono:

  • assegnarlo ad una variabile di riferimento o leggerlo da una variabile di riferimento,
  • assegnarlo ad un elemento di matrix o leggerlo da un elemento di matrix (a condizione che il riferimento di arrays stesso non sia nullo!),
  • passarlo come parametro o restituirlo come risultato, o
  • testarlo usando == o != operatori, o instanceof .

Domanda: Come faccio a leggere la stacktrace NPE?

Supponiamo di compilare ed eseguire 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 del programma NON è un errore di compilazione. È un errore di runtime . (Alcuni IDE possono avvertire il tuo programma butteranno sempre un'exception … ma il compilatore javac standard non lo fa.)

Seconda osservazione: quando eseguo il programma, emette due righe di "gobbledy-gook". SBAGLIATO!! Questo non è gobbledy-gook. È una stacktrace … e fornisce informazioni vitali che ti aiuteranno a individuare l'errore nel tuo codice, se prendi il tempo per leggerlo con attenzione.

Quindi, vediamo cosa si dice:

 Exception in thread "main" java.lang.NullPointerException 

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

  • Ti dice il nome del thread Java in cui è stata lanciata l'exception. Per un programma semplice con un thread (come questo), sarà "principale". Andiamo avanti …
  • Ti dice il nome completo dell'exception che è stata gettata; vale a dire java.lang.NullPointerException .
  • Se l'exception ha un messaggio di errore associato, verrà emesso dopo il nome dell'exception. NullPointerException è inusuale in questo senso perché raramente ha un messaggio di errore.

La seconda row è la più importnte nella diagnosi di un NPE.

 at Test.main(Test.java:4) 

Questo ci dice una serie di cose:

  • "a Test.main" dice che siamo stati nel metodo main della class Test .
  • "Test.java" fornisce il nome del file di origine della class, E ci dice che l'istruzione in cui si è verificato è nella row 4 del file.

E se conta le righe nel file sopra, la row 4 è quella che ho etichettato con il commento "QUI".

Si noti che in un esempio più complicato ci saranno molte linee nella traccia dello stack NPE. Ma puoi essere sicuro che la seconda row (la prima "a") vi dirà where è stato lanciato il NPE 1 .

In breve la stacktrace ci dirà in modo chiaro quale affermazione del programma ha gettato il NPE.

1 – Non è affatto vero. Ci sono cose chiamate eccezioni nidificate …

Domanda: Come faccio a individuare la causa dell'exception NPE nel mio codice?

Questa è la parte difficile. La breve risposta è quella di applicare una deduzione logica alle prove fornite dalla traccia dello stack, dal codice sorgente e dalla relativa documentazione API.

Consente di illustrare innanzitutto il semplice esempio (sopra). Iniziamo guardando la linea che ci ha detto la stacktrace è where è accaduto il NPE:

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

Come può questo lanciare un NPE?

Infatti c'è solo un modo: può accadere solo se foo ha il valore null . Quindi cerchiamo di eseguire il metodo null length() su null e …. BANG!

Ma (lo sento dire) che cosa succede se il NPE è stato lanciato all'interno della chiamata di length() ?

Beh, se fosse successo, la stacktrace sarebbe diversa. La prima row "at" direbbe che l'exception è stata lanciata in una row nella class java.lang.String e la row 4 di Test.java sarebbe la seconda row "a".

Quindi da where è null ? In questo caso è ovvio e è ovvio che cosa dobbiamo fare per risolvere il problema. (Assegna un valore non nullo a foo )

OK, quindi prova a provare un esempio leggermente più difficile. 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) $ 

Adesso abbiamo 2 linee "a". La prima è per questa linea:

  return args[pos].length(); 

e la seconda è per questa linea:

  int length = test(foo, 1); 

Quindi guardando alla prima linea, come poteva lanciare un NPE? Infatti, ci sono due modi:

  • Se il valore della bar è null bar[pos] lancerà un NPE.
  • Se il valore di bar[pos] è null length() chiamata length() su di esso lancerà un NPE.

Quindi, dopo, dobbiamo capire quale di questi scenari spiega cosa sta realmente accadendo. Consente di iniziare esplorando la prima:

Da where viene il bar ? È un parametro alla chiamata del metodo di test e se vediamo come viene chiamato il test , possiamo vedere che viene dalla variabile statica foo . E possiamo vedere chiaramente che abbiamo inizializzato un valore non nullo. Ciò è sufficiente per privare questa spiegazione. (In teoria, qualcos'altro potrebbe cambiare foo a null … ma non succede qui.)

Allora, cosa succede al nostro secondo scenario? Beh possiamo vedere che pos è 1 , quindi significa che foo[1] deve essere null . È ansible?

Certo che lo è! E questo è il problema. Quando inizializziamo così:

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

assegniamo una String[] con due elementi che vengono inizializzati a null . E poi non abbiamo cambiato il contenuto di foo … quindi foo[1] sarà ancora null .

Un'exception null pointer viene causata quando si dereferisce una variabile che punta a null . Vedere il seguente codice:

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

L'exception Null pointer viene lanciata quando un'applicazione tenta di utilizzare null in un caso in cui è necessario un object. Questi includono:

  1. Chiamare il metodo di istanza di un object null .
  2. Accesso o modifica del field di un object null .
  3. Prendendo la lunghezza di null come se fosse un arrays.
  4. Accesso o modifica delle slot di null come se fosse un arrays.
  5. Lanciare null come se fosse un valore Throwable.

Le applicazioni dovrebbero lanciare istanze di questa class per indicare altri usi illeciti dell'object null .

Un puntatore NULL è quello che indica da nessuna parte. Quando si dereference un puntatore p , si dice "date i dati nella posizione memorizzata in" p ". Quando p è un puntatore nullo, la posizione memorizzata in p nowhere è da nowhere , stai dicendo" date i dati nella posizione "da nessuna parte". Ovviamente non può farlo, quindi lancia NULL pointer exception .

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

Molte spiegazioni sono già presenti per spiegare come succede e come risolverlo, ma dovresti anche seguire le migliori pratiche per evitare che NullPointerException affatto.

Vedere anche: Un buon elenco delle migliori pratiche

Vorrei aggiungere, molto importnte, fare un buon uso del modificatore final .
Utilizzando il modificatore "finale" quando applicabile in java

Sommario:

  1. Utilizza il modificatore final per eseguire una buona initialization.
  2. Evitate di restituire null nei methods, ad esempio restituendo le collezioni vuote quando applicabile.
  3. Utilizzare le annotazioni @NotNull e @Nullable
  4. Fai veloce e utilizza gli asserti per evitare la propagazione di oggetti nulli nell'intera applicazione quando non dovrebbero essere nulli.
  5. Utilizzare innanzitutto l'object conosciuto: if("knownObject".equals(unknownObject)
  6. Preferire valueOf() su toString ().
  7. Utilizzare i methods StringUtils.isEmpty(null) sicuri null StringUtils.isEmpty(null) .

In Java each cosa è sotto forma di class.

Se si desidera utilizzare qualsiasi object, si hanno due fasi

  1. Dichiarare
  2. Inizializzazione

Esempio:

  • Dichiarazione: int a;
  • initialization: a=0;

Stesso per il concetto Array

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

Se non viene data la sezione Inizializzazione, si verifica NullpointerException .

Un'output di un puntatore nullo è un indicatore che utilizza Object senza inizializzarlo.

ad esempio sotto è una class studente che usa nel nostro codice.

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

sotto il codice ti dà l'exception puntatore nullo.

 public class School { student Obj_Student; public school() { try { Obj_Student.getId(); }catch(Exception e) { System.out.println("Null Pointer "); } } } 

Poiché si sta utilizzando Obj_Student ma si è dimenticato di inizializzarla come il codice corretto è mostrato sotto

 public class School { student Obj_Student; public school() { try { Obj_Student = new student(); Obj_Student.setId(12); Obj_Student.getId(); }catch(Exception e) { System.out.println("Null Pointer "); } } } 

In Java tutte le variables dichiarate sono in realtà "riferimenti" agli oggetti (o primitivi) e non agli stessi oggetti.

Quando si tenta di eseguire un metodo di object, il riferimento chiede all'object vivente di eseguire tale metodo. Ma se il riferimento fa riferimento a NULL (nulla, zero, void, nada) allora non esiste alcun modo in cui il metodo viene eseguito. Quindi il runtime vi farà conoscere gettando un NullPointerException.

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

L'object vive nello spazio di memory VM e l'unico modo per accedere è quello di utilizzare this riferimenti. Prendi questo esempio:

 public class Some { private int id; public int getId(){ return this.id; } public setId( int newId ) { this.id = newId; } } .... .... // Somewhere else... 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 cosa importnte sapere – quando non ci sono più riferimenti ad un object (nell'esempio precedente quando "riferimento" e "altroReference" indicano null) l'object è "irraggiungibile". Non ci è modo di lavorare con esso, quindi questo object è contrassegnato per essere rifiuti raccolti e ad un certo punto la VM libererà la memory usata da questo object e assegnerà un'altra.

Un'altra occorrenza di una NullPointerException verifica quando si dichiara un arrays di oggetti, quindi cerca immediatamente gli elementi di dereference all'interno di esso.

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

Questo particolare NPE può essere evitato se l'ordine di confronto è invertito; vale a dire, utilizzare .equals su un object non nullo garantito.

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

È necessario inizializzare gli elementi nell'arrays prima di accedere o distriggersrli.

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