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

Resuelto asked hace 15 años • 12 respuestas

¿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 que el programa finalice prematuramente?

 avatar Oct 20 '08 20:10
Aceptado

Hay dos tipos generales de variables en Java:

  1. Primitivas : variables que contienen datos. Si desea manipular los datos en una variable primitiva, puede manipular esa variable directamente. Por convención, los tipos primitivos comienzan con una letra minúscula. Por ejemplo las variables de tipo into charson primitivas.

  2. Referencias : variables que contienen la dirección de memoria de un , Objectes decir, variables que hacen referencia a un Object. Si desea manipular la Objectreferencia a la que se refiere una variable de referencia, debe eliminar la referencia. La desreferenciación generalmente implica usar .para acceder a un método o campo, o usar [para indexar una matriz. Por convención, los tipos de referencia generalmente se indican con un tipo que comienza en mayúscula. Por ejemplo, las variables de tipo Objectson referencias.

Considere el siguiente código donde declara una variable de tipo primitivoint y no la inicializa:

int x;
int y = x + x;

Estas dos líneas bloquearán el programa porque no se especifica ningún valor xy estamos intentando utilizar xel valor de para especificar y. Todas las primitivas deben inicializarse a un valor utilizable antes de manipularlas.

Ahora aquí es donde las cosas se ponen interesantes. Las variables de referencia se pueden configurar para nullque signifique " No estoy haciendo referencia a nada ". Puede obtener un nullvalor en una variable de referencia si lo establece explícitamente de esa manera, o si una variable de referencia no está inicializada y el compilador no la detecta (Java establecerá automáticamente la variable en null).

Si una variable de referencia se establece en nula, ya sea explícitamente por usted o a través de Java automáticamente, e intenta desreferenciarla , obtendrá un archivo NullPointerException.

El NullPointerException(NPE) normalmente ocurre cuando declara una variable pero no crea un objeto ni lo asigna a la variable antes de intentar usar el contenido de la variable. Entonces tienes una referencia a algo que en realidad no existe.

Tome el siguiente código:

Integer num;
num = new Integer(10);

La primera línea declara una variable llamada num, pero en realidad aún no contiene un valor de referencia. Como aún no ha dicho qué señalar, Java lo establece en null.

En la segunda línea, la newpalabra clave se usa para crear una instancia (o crear) un objeto de tipo Integery la variable de referencia numse asigna a ese Integerobjeto.

Si intenta eliminar la referencia num antes de crear el objeto, obtendrá un archivo NullPointerException. En los casos más triviales, el compilador detectará el problema y le informará 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 como el siguiente:

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 que asume que se creó antes de que doSomething()se llamara al método. Tenga en cuenta que es posible llamar al método de esta manera:

doSomething(null);

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

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

Además de NullPointerExceptionlos mensajes de correo electrónico lanzados como resultado de la lógica del método, también puede verificar los argumentos del método en busca de nullvalores y generar 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 indicar claramente en el mensaje de error qué objeto no puede ser null. La ventaja de validar esto 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, deberá verificar si hay un parámetro nulo y comportarse de manera diferente. También deberías 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 que el programa finalice prematuramente?

Soñar con encontrar bichos puede detectar NPE. ¿Puede la sonda detectar excepciones de puntero nulo causadas por JVM dinámicamente?

Ahora Java 14 ha agregado una nueva característica de lenguaje para mostrar la causa raíz de NullPointerException. Esta característica de lenguaje ha sido parte de la JVM comercial de SAP desde 2006.

En Java 14, el siguiente es un mensaje de excepción NullPointerException de ejemplo:

en el hilo "principal" java.lang.NullPointerException: no se puede invocar "java.util.List.size()" porque "lista" es nula

Lista de situaciones que provocan NullPointerExceptionque ocurra una

Aquí están todas las situaciones en las que NullPointerExceptionocurre a, que son mencionadas directamente* por la Especificación del lenguaje Java:

  • Acceder (es decir, obtener o configurar) un campo de instancia de una referencia nula. (¡Los campos estáticos no cuentan!)
  • Llamar a un método de instancia de una referencia nula. (¡Los métodos estáticos no cuentan!)
  • throw null;
  • Accediendo a elementos de una matriz nula.
  • Sincronizando en nulo -synchronized (someNullReference) { ... }
  • Cualquier operador de número entero/coma flotante puede generar a NullPointerExceptionsi uno de sus operandos es una referencia nula encuadrada
  • Una conversión de unboxing arroja a NullPointerExceptionsi el valor del cuadro es nulo.
  • Llamar supera una referencia nula genera un archivo NullPointerException. Si está confundido, esto se refiere a invocaciones calificadas de constructores de superclases:
class Outer {
    class Inner {}
}
class ChildOfInner extends Outer.Inner {
    ChildOfInner(Outer o) { 
        o.super(); // if o is null, NPE gets thrown
    }
}
  • Usar un for (element : iterable)bucle para recorrer una colección/matriz nula.

  • switch (foo) { ... }(ya sea una expresión o una declaración) puede generar un NullPointerExceptioncuando fooes nulo.

  • foo.new SomeInnerClass()arroja un NullPointerExceptioncuando fooes nulo.

  • Referencias de método del formulario name1::name2o primaryExpression::namearroja un NullPointerExceptioncuando se evalúa cuando name1o primaryExpressionse evalúa como nulo.

    Una nota del JLS aquí dice que someInstance.someStaticMethod()no arroja un NPE porque someStaticMethodes estático, ¡pero someInstance::someStaticMethodaún así arroja un NPE!

* Tenga en cuenta que el JLS probablemente también dice mucho sobre las NPE de forma indirecta .

Vincent Ramdhanie avatar Oct 20 '2008 13:10 Vincent Ramdhanie

NullPointerExceptions son excepciones que ocurren cuando intentas 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 archivo NullPointerException. Estas son las más comunes, pero se enumeran otras formas en la NullPointerExceptionpágina de javadoc.

Probablemente el código de ejemplo más rápido que se me ocurrió para ilustrar 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 sobre él. Esto da como resultado un error NullPointerExceptionporque no hay ningún 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 de apuntando a una ubicación que resulta no válida.)

Bill the Lizard avatar Oct 20 '2008 13:10 Bill the Lizard

¿Qué es una excepción NullPointerException?

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

Se lanza cuando una aplicación intenta usar null en un caso en el que se requiere un objeto. Éstas incluyen:

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

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

También se da el caso de que si intenta utilizar una referencia nula con synchronized, eso también generará esta excepción, según JLS :

SynchronizedStatement:
    synchronized ( Expression ) Block
  • De lo contrario, si el valor de la expresión es nulo, NullPointerExceptionse genera 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 provocando la excepción . Para esto, necesitamos hacer algo de depuración. Es importante aprender a leer un seguimiento de pila . 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 al llamar al lengthmétodo se genera la excepción. Podemos ver que el programa deja de lanzar la excepción cuando s.length()se elimina del método.

Rastree de dónde provienen estos valores

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

Traza dónde se deben establecer estos valores

¿ Dónde está this.namefijado? 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 que el método set no se llame después del método print.

Esto es suficiente para darnos una solución: agregar una llamada 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 = "";

Cualquiera de los dos métodos printo printStringpuede comprobar si hay valores nulos , 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 probado hasta ahora. Como mínimo, incluya el seguimiento de la pila en la pregunta y marque los números de línea importantes en el código. Además, intente simplificar el código primero (consulte SSCCE ).

fgb avatar Jun 07 '2014 19:06 fgb

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

Como debes saber, los tipos de Java se dividen en tipos primitivos ( boolean,, intetc.) y tipos de referencia . Los tipos de referencia en Java le permiten utilizar el valor especial nullque es la forma Java de decir "sin objeto".

A NullPointerExceptionse lanza en tiempo de ejecución cada vez que su programa intenta utilizar a nullcomo si fuera una referencia real. Por ejemplo, si escribes 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 generará un archivo NullPointerException.

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

  • asignarlo a una variable de referencia o leerlo de una variable de referencia,
  • asignarlo a un elemento de matriz o leerlo desde un elemento de matriz (¡siempre que la referencia de matriz en sí no sea nula!),
  • pasarlo como parámetro o devolverlo como resultado, o
  • pruébelo usando los operadores ==o , o .!=instanceof

Pregunta: ¿Cómo leo el seguimiento de la pila de 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 es un é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 generará una excepción... pero el javaccompilador estándar no).

Segunda observación: cuando ejecuto el programa, genera dos líneas de "gobbledy-gook". ¡¡EQUIVOCADO!! Eso no es una tontería. 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 detenidamente.

Así que veamos lo que dice:

Exception in thread "main" java.lang.NullPointerException

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

  • Le indica 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 indica 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 mostrará después del nombre de la excepción. NullPointerExceptiones inusual a este respecto, porque rara vez muestra un mensaje de error.

La segunda línea es la más importante en el diagnóstico de una NPE.

at Test.main(Test.java:4)

Esto nos dice una serie de cosas:

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

Si cuentas las líneas del archivo anterior, 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 puedes estar seguro de que la segunda línea (la primera línea "arroba") te indicará dónde se lanzó la NPE 1 .

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

Ver también: ¿Qué es un seguimiento de pila y cómo puedo usarlo para depurar los errores de mi aplicación?

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

Pregunta: ¿Cómo localizo 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 API relevante.

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

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

¿Cómo puede eso generar una NPE?

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

Pero (te oigo decir) ¿qué pasaría 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 produjo 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, probemos con un ejemplo un poco más complicado. Esto requerirá alguna 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)
$ 

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 eso generar una NPE? Hay dos maneras:

  • Si el valor de bares nullentonces bar[pos]arrojará una NPE.
  • Si el valor de bar[pos]es, nullentonces llamar length()arrojará una NPE.

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

¿ De dónde barviene? Es un parámetro de 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 foocon un valor no nulo. Esto 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 así 1, entonces eso significa que foo[1]debe ser así 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... así que foo[1]seguirá siendo así null.

¿Qué pasa con Android?

En Android, localizar la causa inmediata de una NPE es un poco más sencillo. El mensaje de excepción normalmente le indicará el tipo (tiempo de compilación) de la referencia nula que está utilizando y el método que intentaba llamar cuando se lanzó el NPE. Esto simplifica el proceso de identificar la causa inmediata.

Pero, por otro lado, Android tiene algunas causas comunes específicas de la plataforma para las NPE. Una cosa muy común es cuando getViewByIdinesperadamente devuelve un archivo null. Mi consejo sería buscar preguntas y respuestas sobre la causa del nullvalor de retorno inesperado.

Stephen C avatar Jun 22 '2014 02:06 Stephen C

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 que contenga, arrojará NullPointerExceptionlo que tiene sentido.

Vea también este ejemplo a continuación:

String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
Rakesh Burbure avatar Oct 20 '2008 13:10 Rakesh Burbure