¿Qué es una NullPointerException y cómo la soluciono?

Oct 20 2008

¿Qué son las excepciones de puntero nulo ( java.lang.NullPointerException) y qué las causa?

¿Qué métodos / herramientas se pueden utilizar para determinar la causa y evitar que la excepción provoque la finalización prematura del programa?

Respuestas

3848 VincentRamdhanie Oct 20 2008 at 20:54

Cuando declaras una variable de referencia (es decir, un objeto), en realidad estás creando un puntero a un objeto. Considere el siguiente código donde declara una variable de tipo primitivo int:

int x;
x = 10;

En este ejemplo, la variable xes an inty Java la inicializará automáticamente 0. Cuando le asigna el valor de 10en la segunda línea, su valor de 10se escribe en la ubicación de memoria a la que hace referencia x.

Pero, cuando intenta declarar un tipo de referencia , sucede algo diferente. Toma el siguiente código:

Integer num;
num = new Integer(10);

La primera línea declara una variable nombrada num, pero todavía no contiene un valor primitivo. En su lugar, contiene un puntero (porque el tipo es el Integerque es un tipo de referencia). Como aún no ha dicho a qué apuntar, Java lo establece en null, lo que significa " No estoy apuntando a nada ".

En la segunda línea, la newpalabra clave se usa para instanciar (o crear) un objeto de tipo Integer, y la variable de puntero numse asigna a ese Integerobjeto.

La NullPointerExceptionocurre cuando se declara una variable, pero no creó un objeto y la asigna a la variable antes de intentar utilizar el contenido de la variable (llamada desreferencia ). Entonces estás apuntando a algo que en realidad no existe.

La desreferenciación generalmente ocurre cuando se usa .para acceder a un método o campo, o cuando se usa [para indexar una matriz.

Si intenta eliminar la referencia numANTES de crear el objeto, obtiene un NullPointerException. En los casos más triviales, el compilador detectará el problema y le hará saber que " num may not have been initialized," pero a veces puede escribir código que no crea directamente el objeto.

Por ejemplo, puede tener un método de la siguiente manera:

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

En cuyo caso, no está creando el objeto obj, sino asumiendo que se creó antes de doSomething()que se llamara al método. Tenga en cuenta que es posible llamar al método de esta manera:

doSomething(null);

En cuyo caso, objes null, y la declaración obj.myMethod()arrojará un NullPointerException.

Si el método tiene la intención de hacer algo con el objeto pasado como lo hace el método anterior, es apropiado lanzar el NullPointerExceptionporque es un error del programador y el programador necesitará esa información para propósitos de depuración.

Además de los NullPointerExceptions lanzados como resultado de la lógica del método, también puede verificar los argumentos del método en busca de nullvalores y lanzar NPE explícitamente agregando algo como lo siguiente cerca del comienzo de un método:

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

Tenga en cuenta que es útil decir claramente en su mensaje de error qué objeto no puede ser null. La ventaja de realizar una validación de esta manera es que 1) puede devolver sus propios mensajes de error más claros y 2) para el resto del método, sabe que, a menos que objse reasigne, no es nulo y se puede desreferenciar de forma segura.

Alternativamente, puede haber casos en los que el propósito del método no sea únicamente operar en el objeto pasado y, por lo tanto, un parámetro nulo puede ser aceptable. En este caso, necesitaría verificar un parámetro nulo y comportarse de manera diferente. También debe explicar esto en la documentación. Por ejemplo, doSomething()podría escribirse como:

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

Finalmente, cómo identificar la excepción y la causa usando Stack Trace

¿Qué métodos / herramientas se pueden utilizar para determinar la causa y evitar que la excepción provoque la finalización prematura del programa?

La sonda con búsqueda de errores puede detectar NPE. ¿Puede la sonda detectar las excepciones de puntero nulo causadas por JVM de forma dinámica?

Ahora Java 14 ha agregado una nueva función de lenguaje para mostrar la causa raíz de NullPointerException. Esta característica de idioma ha sido parte de la JVM comercial de SAP desde 2006. A continuación, se leen 2 minutos para comprender esta característica de idioma asombrosa.

https://jfeatures.com/blog/NullPointerException

En java 14, a continuación, se muestra un mensaje de ejemplo de excepción NullPointerException:

en el hilo "main" java.lang.NullPointerException: No se puede invocar "java.util.List.size ()" porque "list" es nulo

898 BilltheLizard Oct 20 2008 at 20:20

NullPointerExceptionLos s son excepciones que ocurren cuando intenta usar una referencia que no apunta a ninguna ubicación en la memoria (nula) como si estuviera haciendo referencia a un objeto. Llamar a un método en una referencia nula o intentar acceder a un campo de una referencia nula activará un NullPointerException. Estas son las más comunes, pero en la NullPointerExceptionpágina de javadoc se enumeran otras formas .

Probablemente, el código de ejemplo más rápido que se me ocurrió para ilustrar un NullPointerExceptionsería:

public class Example {

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

}

En la primera línea interior main, establezco explícitamente la Objectreferencia objigual a null. Esto significa que tengo una referencia, pero no apunta a ningún objeto. Después de eso, trato de tratar la referencia como si apuntara a un objeto llamando a un método. Esto da como resultado un NullPointerExceptionporque no hay código para ejecutar en la ubicación a la que apunta la referencia.

(Esto es un tecnicismo, pero creo que vale la pena mencionarlo: una referencia que apunta a nulo no es lo mismo que un puntero C que apunta a una ubicación de memoria no válida. Un puntero nulo literalmente no apunta a ninguna parte , lo cual es sutilmente diferente a apuntando a una ubicación que resulta no ser válida).

709 fgb Jun 08 2014 at 02:22

¿Qué es una NullPointerException?

Un buen lugar para comenzar son los JavaDocs . Tienen esto cubierto:

Se lanza cuando una aplicación intenta utilizar un valor nulo en un caso en el que se requiere un objeto. Éstos incluyen:

  • Llamar al método de instancia de un objeto nulo.
  • Acceder o modificar el campo de un objeto nulo.
  • Tomando la longitud de nulo como si fuera una matriz.
  • Accediendo o modificando los slots de null como si fuera un array.
  • Lanzar nulo como si fuera un valor Throwable.

Las aplicaciones deben lanzar instancias de esta clase para indicar otros usos ilegales del objeto nulo.

También es el caso de que si intenta usar una referencia nula con synchronized, eso también arrojará esta excepción, según JLS :

SynchronizedStatement:
    synchronized ( Expression ) Block
  • De lo contrario, si el valor de la expresión es nulo, NullPointerExceptionse lanza a.

¿Cómo lo soluciono?

Entonces tienes un NullPointerException. Como lo arreglas? Tomemos un ejemplo simple que arroja 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();
    }
}

Identificar los valores nulos

El primer paso es identificar exactamente qué valores están causando la excepción . Para esto, necesitamos hacer una depuración. Es importante aprender a leer un stacktrace . Esto le mostrará dónde se lanzó la excepción:

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)

Aquí, vemos que la excepción se lanza en la línea 13 (en el printStringmétodo). Mire la línea y verifique qué valores son nulos agregando declaraciones de registro o usando un depurador . Descubrimos que ses nulo, y llamar al lengthmétodo arroja la excepción. Podemos ver que el programa deja de lanzar la excepción cuando s.length()se elimina del método.

Rastrear de dónde provienen estos valores

A continuación, compruebe de dónde proviene este valor. Al seguir a los llamadores del método, vemos que sse pasa con printString(name)en el print()método y this.namees nulo.

Rastrear dónde se deben establecer estos valores

¿Dónde se this.nameestablece? En el setName(String)método. Con un poco más de depuración, podemos ver que este método no se llama en absoluto. Si se llamó al método, asegúrese de verificar el orden en que se llaman estos métodos, y el método set no se llama después del método de impresión.

Esto es suficiente para darnos una solución: agregue una llamada a printer.setName()antes de llamar printer.print().

Otras correcciones

La variable puede tener un valor predeterminado (y setNamepuede evitar que se establezca en nulo):

private String name = "";

O el método printo printStringpuede verificar si hay nulo , por ejemplo:

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

O puede diseñar la clase para que name siempre tenga un valor no nulo :

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

Ver también:

  • ¿Evitar declaraciones "! = Null" en Java?

Todavía no puedo encontrar el problema

Si intentó depurar el problema y aún no tiene una solución, puede publicar una pregunta para obtener más ayuda, pero asegúrese de incluir lo que ha intentado hasta ahora. Como mínimo, incluya el stacktrace en la pregunta y marque los números de línea importantes en el código. Además, primero intente simplificar el código (consulte SSCCE ).

516 StephenC Jun 22 2014 at 09:16

Pregunta: ¿Qué causa una NullPointerException(NPE)?

Como usted debe saber, tipos de Java se dividen en tipos primitivos ( boolean, int, etc.) y los tipos de referencia . Los tipos de referencia en Java le permiten usar el valor especial nullque es la forma en que Java de decir "ningún objeto".

A NullPointerExceptionse lanza en tiempo de ejecución cada vez que su programa intenta usar a nullcomo si fuera una referencia real. Por ejemplo, si escribe esto:

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

la declaración etiquetada "AQUÍ" intentará ejecutar el length()método en una nullreferencia, y esto arrojará un NullPointerException.

Hay muchas formas en las que puede usar un nullvalor que dará como resultado un NullPointerException. De hecho, las únicas cosas que puede hacer con un nullsin causar una NPE son:

  • asignarlo a una variable de referencia o leerlo de una variable de referencia,
  • asígnelo a un elemento de matriz o léalo de un elemento de matriz (¡siempre que la referencia de matriz en sí no sea nula!),
  • pasarlo como parámetro o devolverlo como resultado, o
  • probarlo con los ==u !=operadores, o instanceof.

Pregunta: ¿Cómo leo el stacktrace NPE?

Supongamos que compilo y ejecuto el programa anterior:

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

Primera observación: ¡la compilación tiene éxito! El problema en el programa NO es un error de compilación. Es un error de tiempo de ejecución . (Algunos IDE pueden advertir que su programa siempre lanzará una excepción ... pero el javaccompilador estándar no lo hace).

Segunda observación: cuando ejecuto el programa, genera dos líneas de "gobbledy-gook". ¡¡EQUIVOCADO!! Eso no es tonterías. Es un seguimiento de pila ... y proporciona información vital que le ayudará a localizar el error en su código si se toma el tiempo de leerlo con atención.

Así que veamos lo que dice:

Exception in thread "main" java.lang.NullPointerException

La primera línea del seguimiento de la pila le dice varias cosas:

  • Le dice el nombre del hilo de Java en el que se lanzó la excepción. Para un programa simple con un hilo (como este), será "principal". Vamonos ...
  • Le dice el nombre completo de la excepción que se lanzó; es decir java.lang.NullPointerException.
  • Si la excepción tiene un mensaje de error asociado, se emitirá después del nombre de la excepción. NullPointerExceptiones inusual a este respecto, porque rara vez tiene un mensaje de error.

La segunda línea es la más importante para diagnosticar una NPE.

at Test.main(Test.java:4)

Esto nos dice varias cosas:

  • "at Test.main" dice que estábamos en el mainmétodo de la Testclase.
  • "Test.java:4" da el nombre de archivo fuente de la clase, Y nos dice que la declaración donde ocurrió esto está en la línea 4 del archivo.

Si cuenta las líneas en el archivo de arriba, la línea 4 es la que etiqueté con el comentario "AQUÍ".

Tenga en cuenta que en un ejemplo más complicado, habrá muchas líneas en el seguimiento de la pila NPE. Pero puede estar seguro de que la segunda línea (la primera línea "arroba") le dirá dónde se lanzó la NPE 1 .

En resumen, el seguimiento de la pila nos dirá sin ambigüedades qué declaración del programa ha arrojado el NPE.

1 - No es del todo cierto. Hay cosas llamadas excepciones anidadas ...

Pregunta: ¿Cómo puedo localizar la causa de la excepción NPE en mi código?

Ésta es la parte difícil. La respuesta corta es aplicar la inferencia lógica a la evidencia proporcionada por el seguimiento de la pila, el código fuente y la documentación de la API relevante.

Ilustremos primero con el ejemplo simple (arriba). Comenzamos mirando la línea que el seguimiento de la pila nos ha dicho que es donde ocurrió la NPE:

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

¿Cómo puede eso lanzar una NPE?

De hecho, solo hay una forma: solo puede suceder si footiene el valor null. Luego intentamos ejecutar el length()método nully ... ¡BANG!

Pero (te escucho decir) ¿y si el NPE se lanzara dentro de la length()llamada al método?

Bueno, si eso sucediera, el seguimiento de la pila se vería diferente. La primera línea "arroba" diría que la excepción se lanzó en alguna línea de la java.lang.Stringclase y la línea 4 de Test.javasería la segunda línea "arroba".

Entonces, ¿de dónde vino eso null? En este caso, es obvio y es obvio lo que debemos hacer para solucionarlo. (Asigne un valor no nulo a foo.)

Bien, intentemos con un ejemplo un poco más complicado. Esto requerirá una deducción lógica .

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

Así que ahora tenemos dos líneas "arroba". El primero es para esta línea:

return args[pos].length();

y el segundo es para esta línea:

int length = test(foo, 1);

Mirando la primera línea, ¿cómo podría arrojar una NPE? Hay dos maneras:

  • Si el valor de bares nullentonces bar[pos]lanzará una NPE.
  • Si el valor de bar[pos]es null, la invocación length()arrojará un NPE.

A continuación, debemos averiguar cuál de esos escenarios explica lo que realmente está sucediendo. Comenzaremos explorando el primero:

De donde barviene Es un parámetro para la testllamada al método, y si miramos cómo testse llamó, podemos ver que proviene de la foovariable estática. Además, podemos ver claramente que inicializamos fooa un valor no nulo. Eso es suficiente para descartar tentativamente esta explicación. (En teoría, algo más podría cambiar foo a null... pero eso no está sucediendo aquí).

Entonces, ¿qué pasa con nuestro segundo escenario? Bueno, podemos ver que poses 1así, eso significa que foo[1]debe ser null. es posible?

¡De hecho, es! Y ese es el problema. Cuando inicializamos así:

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

asignamos a String[]con dos elementos que se inicializan ennull . Después de eso, no hemos cambiado el contenido de foo... por foo[1]lo que todavía lo haremos null.

432 RakeshBurbure Oct 20 2008 at 20:21

Es como si estuvieras intentando acceder a un objeto que es null. Considere el siguiente ejemplo:

TypeA objA;

En este momento, acaba de declarar este objeto, pero no lo ha inicializado ni creado una instancia . Y cada vez que intente acceder a cualquier propiedad o método en él, arrojará, lo NullPointerExceptionque tiene sentido.

Vea también este ejemplo a continuación:

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

Se lanza una excepción de puntero nulo cuando una aplicación intenta utilizar nulo en un caso en el que se requiere un objeto. Éstos incluyen:

  1. Llamar al método de instancia de un nullobjeto.
  2. Acceder o modificar el campo de un nullobjeto.
  3. Tomando la longitud de nullcomo si fuera una matriz.
  4. Accediendo o modificando las ranuras de nullcomo si fuera un arreglo.
  5. Lanzar nullcomo si fuera un valor arrojable.

Las aplicaciones deben lanzar instancias de esta clase para indicar otros usos ilegales del nullobjeto.

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

341 MrZebra Oct 20 2008 at 20:25

Un nullpuntero es aquel que apunta a ninguna parte. Cuando eliminas la referencia a un puntero p, dices "dame los datos en la ubicación almacenada en" p ". Cuando pes un nullpuntero, la ubicación almacenada en pes nowhere, estás diciendo" dame los datos en la ubicación 'en ninguna parte' ". Obviamente, no puede hacer esto, por lo que arroja un null pointer exception.

En general, se debe a que algo no se ha inicializado correctamente.

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

Ya hay muchas explicaciones para explicar cómo sucede y cómo solucionarlo, pero también debe seguir las mejores prácticas para evitar los NullPointerExceptions.

Ver también: Una buena lista de mejores prácticas

Yo añadiría, muy importante, hacer un buen uso del finalmodificador. Usar el modificador "final" siempre que sea aplicable en Java

Resumen:

  1. Utilice el finalmodificador para imponer una buena inicialización.
  2. Evite devolver nulos en los métodos, por ejemplo, devolver colecciones vacías cuando corresponda.
  3. Utilice anotaciones @NotNully@Nullable
  4. Falle rápido y use afirmaciones para evitar la propagación de objetos nulos a través de toda la aplicación cuando no deberían ser nulos.
  5. Primero use iguales con un objeto conocido: if("knownObject".equals(unknownObject)
  6. Preferir valueOf()sobre toString().
  7. Utilice StringUtilsmétodos nulos de seguridad StringUtils.isEmpty(null).
  8. Use Java 8 Optional como valor de retorno en los métodos, la clase Optional proporciona una solución para representar valores opcionales en lugar de referencias nulas.
323 ashishbhatt Jan 28 2012 at 13:45

En Java, todo (excepto los tipos primitivos) tiene la forma de una clase.

Si desea utilizar cualquier objeto, tiene dos fases:

  1. Declarar
  2. Inicialización

Ejemplo:

  • Declaración: Object object;
  • Inicialización: object = new Object();

Lo mismo para el concepto de matriz:

  • Declaración: Item item[] = new Item[5];
  • Inicialización: item[0] = new Item();

Si no está dando la sección de inicialización, entonces NullPointerExceptionsurgen.

322 javidpiprani Sep 24 2013 at 13:01

Una excepción de puntero nulo es un indicador de que está utilizando un objeto sin inicializarlo.

Por ejemplo, a continuación se muestra una clase de estudiantes que la usará en nuestro código.

public class Student {

    private int id;

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

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

El siguiente código le da una excepción de puntero nulo.

public class School {

    Student student;

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

Porque está usando student, pero olvidó inicializarlo como en el código correcto que se muestra a continuación:

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

En Java, todas las variables que declaras son en realidad "referencias" a los objetos (o primitivas) y no a los objetos en sí.

Cuando intenta ejecutar un método de objeto, la referencia le pide al objeto vivo que ejecute ese método. Pero si la referencia hace referencia a NULL (nada, cero, vacío, nada), entonces no hay forma de que se ejecute el método. Luego, el tiempo de ejecución le permite saber esto lanzando una NullPointerException.

Su referencia es "apuntando" a nulo, por lo tanto, "Nulo -> Puntero".

El objeto vive en el espacio de memoria de la máquina virtual y la única forma de acceder a él es mediante thisreferencias. Toma este ejemplo:

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

Y en otro lugar de tu código:

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

Es importante saber esto: cuando no hay más referencias a un objeto (en el ejemplo anterior cuando referencey otherReferenceambos apuntan a nulo), el objeto es "inalcanzable". No hay forma de que podamos trabajar con él, por lo que este objeto está listo para ser recolectado como basura y, en algún momento, la VM liberará la memoria utilizada por este objeto y asignará otro.

287 Makoto May 25 2014 at 13:11

Otra ocurrencia de a NullPointerExceptionocurre cuando uno declara una matriz de objetos, luego intenta inmediatamente desreferenciar elementos dentro de ella.

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

Esta NPE en particular se puede evitar si se invierte el orden de comparación; es decir, utilizar .equalsen un objeto no nulo garantizado.

Todos los elementos dentro de una matriz se inicializan a su valor inicial común ; para cualquier tipo de matriz de objetos, eso significa que todos los elementos son null.

Usted debe inicializar los elementos de la matriz antes de acceder o eliminación de referencias a ellos.

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