Respuestas:
Cuando declara una variable de referencia (es decir, un objeto), realmente está 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 x
es an int
y Java la inicializará 0
por usted. Cuando le asigna el valor de 10
en la segunda línea, su valor de 10
se escribe en la ubicación de memoria mencionada por 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 llamada num
, pero aún no contiene un valor primitivo. En cambio, contiene un puntero (porque el tipo es el Integer
que es un tipo de referencia). Como todavía no ha dicho a qué apuntar, Java lo establece null
, lo que significa " no estoy apuntando a nada ".
En la segunda línea, la new
palabra clave se usa para crear instancias (o crear) un objeto de tipo Integer
y la variable de puntero num
se asigna a ese Integer
objeto.
Esto NullPointerException
ocurre cuando declaras una variable pero no creas un objeto y la asignas a la variable antes de intentar usar el contenido de la variable (llamada desreferenciación ). Entonces estás señalando algo que en realidad no existe.
La desreferencia generalmente ocurre cuando se usa .
para acceder a un método o campo, o cuando se usa [
para indexar una matriz.
Si intenta desreferenciar num
ANTES de crear el objeto, obtendrá 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
}
En cuyo caso, no está creando el objeto obj
, sino asumiendo que fue creado 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, obj
es null
. Si el método pretende hacer algo al objeto pasado, es apropiado lanzarlo NullPointerException
porque es un error del programador y el programador necesitará esa información para fines de depuración. Incluya el nombre de la variable del objeto en el mensaje de excepción, como
Objects.requireNonNull(a, "a");
Alternativamente, puede haber casos en los que el propósito del método no sea únicamente operar sobre 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 usar para determinar la causa de modo que evite que la excepción haga que el programa finalice prematuramente?
La sonda con findbugs puede detectar NPE. ¿Puede la sonda detectar excepciones de puntero nulo causadas por JVM dinámicamente?
int a=b
puede arrojar un NPE si b es un Integer
. Hay casos en los que esto es confuso para depurar.
NullPointerException
problemas en su código es para uso @Nullable
y @NotNull
anotaciones. La siguiente respuesta tiene más información sobre esto. Aunque esta respuesta se refiere específicamente al IDE de IntelliJ, también es aplicable a otras herramientas, como se muestra en los comentarios. (Por cierto, no se me permite editar esta respuesta directamente, ¿tal vez el autor puede agregarla?)
NullPointerException
s son excepciones que ocurren cuando intenta utilizar una referencia que apunta a ninguna ubicación en la memoria (nulo) 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á a NullPointerException
. Estos son los más comunes, pero se enumeran otras formas en la NullPointerException
página de javadoc.
Probablemente el código de ejemplo más rápido que se me ocurrió para ilustrar un NullPointerException
sería:
public class Example {
public static void main(String[] args) {
Object obj = null;
obj.hashCode();
}
}
En la primera línea adentro main
, estoy configurando explícitamente elObject
referencia obj
igual 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 NullPointerException
porque 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 mencionar: 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 , que es sutilmente diferente de apuntando a una ubicación que resulta no válida).
null
antes de usarla, como este . Con las variables locales, el compilador detectaría este error, pero en este caso no lo hace. ¿Tal vez eso sería una adición útil a su respuesta?
Un buen lugar para comenzar es los JavaDocs . Tienen esto cubierto:
Se lanza cuando una aplicación intenta usar nulo en un caso donde se requiere un objeto. Éstos incluyen:
- Llamando 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.
- Acceder o modificar las ranuras de nulo como si fuera una matriz.
- Lanzamiento nulo como si fuera un valor Lanzable.
Las aplicaciones deberían lanzar instancias de esta clase para indicar otros usos ilegales del objeto nulo.
También es el caso de que si intenta utilizar una referencia nula con synchronized
, eso también arrojará esta excepción, según el JLS :
SynchronizedStatement: synchronized ( Expression ) Block
- De lo contrario, si el valor de la Expresión es nulo,
NullPointerException
se arroja a.
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 alguna depuración. Es importante aprender a leer un stacktrace . Esto le mostrará dónde se produjo 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 printString
método). Mire la línea y verifique qué valores son nulos agregando declaraciones de registro o utilizando un depurador . Descubrimos que s
es nulo, y llamar al length
método en él arroja 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
Luego verifique de dónde proviene este valor. Al seguir los llamadores del método, vemos que s
se pasa con printString(name)
el print()
método y this.name
es nulo.
Rastrear dónde deben establecerse estos valores
¿Dónde se this.name
establece? En el setName(String)
metodo. 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 llama a estos métodos, y el método establecido no se llama después del método print.
Esto es suficiente para darnos una solución: agregue una llamada printer.setName()
antes de llamarprinter.print()
.
La variable puede tener un valor predeterminado (y setName
puede evitar que se establezca como nulo):
private String name = "";
El método print
o printString
puede verificar 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:
Si trató de 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, intente simplificar el código primero (vea SSCCE ).
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 utilizar el valor especial, null
que es la forma en que Java dice "sin objeto".
A NullPointerException
se lanza en tiempo de ejecución cada vez que su programa intenta usar un null
como 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 null
referencia, y esto arrojará unNullPointerException
.
Hay muchas formas en que podría usar un null
valor que resultará en a NullPointerException
. De hecho, las únicas cosas que puede hacer null
sin causar un NPE son:
==
u !=
operadores, o instanceof
.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 IDEs pueden advertir que su programa siempre arrojará una excepción ... pero el estándarjavac
compilador no lo hace).
Segunda observación: cuando ejecuto el programa, genera dos líneas de "gobbledy-gook". ¡¡INCORRECTO!! Eso no es gobbledy-gook. Es un stacktrace ... y proporciona información vital. que lo ayudará a rastrear el error en su código si se toma el tiempo de leerlo cuidadosamente.
Así que echemos un vistazo a lo que dice:
Exception in thread "main" java.lang.NullPointerException
La primera línea del seguimiento de la pila te dice varias cosas:
java.lang.NullPointerException
.NullPointerException
es inusual a este respecto, porque rara vez tiene un mensaje de error.La segunda línea es la más importante para diagnosticar un NPE.
at Test.main(Test.java:4)
Esto nos dice una serie de cosas:
main
método de la Test
clase.Si cuenta las líneas en el 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 puede estar seguro de que la segunda línea (la primera línea "en") le dirá dónde se arrojó el NPE 1 .
En resumen, el seguimiento de la pila nos dirá inequívocamente qué afirmación del programa ha arrojado el NPE.
1 - No del todo cierto. Hay cosas llamadas excepciones anidadas ...
Esta es la parte difícil. La respuesta corta es aplicar inferencia lógica a la evidencia proporcionada por el seguimiento de la pila, el código fuente y la documentación API relevante.
Vamos a ilustrar primero con el ejemplo simple (arriba). Comenzamos mirando la línea que el seguimiento de la pila nos ha dicho es donde ocurrió el NPE:
int length = foo.length(); // HERE
¿Cómo puede eso arrojar un NPE?
De hecho, solo hay una forma: solo puede suceder si foo
tiene el valor null
. Luego intentamos ejecutar el length()
método null
y ... ¡BANG!
Pero (te escucho decir) qué pasa si el NPE se arrojó dentro del length()
llamada método?
Bueno, si eso sucediera, el seguimiento de la pila se vería diferente. La primera línea "en" diría que la excepción se lanzó en alguna línea de la java.lang.String
clase y en la línea 4 deTest.java
sería la segunda línea "en".
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 afoo
).
Bien, intentemos 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)
$
Así que ahora tenemos dos líneas "en". 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 arrojar un NPE? Hay dos maneras:
bar
es null
entonces bar[pos]
arrojará un NPE.bar[pos]
se null
entonces llama length()
en él arrojará una NPE.A continuación, tenemos que averiguar cuál de esos escenarios explica lo que realmente está sucediendo. Comenzaremos explorando el primero:
De donde bar
viene Es un parámetro para la test
llamada al método, y si observamos cómo test
se llamó, podemos ver que proviene de la foo
variable estática. Además, podemos ver claramente que inicializamos foo
a un valor no nulo. Eso es suficiente para descartar provisionalmente esta explicación. (En teoría, algo más podría cambiar foo
anull
... pero eso no está sucediendo aquí).
Entonces, ¿qué pasa con nuestro segundo escenario? Bueno, podemos ver que pos
es 1
, lo que 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 inicializannull
. Después de eso, no hemos cambiado el contenido de foo
... así foo[1]
será null
.
Es como si estuviera intentando acceder a un objeto que es null
. Considere el siguiente ejemplo:
TypeA objA;
En este momento, acaba de declarar este objeto pero no se ha inicializado o instanciado . Y cada vez que intente acceder a cualquier propiedad o método, arrojará lo NullPointerException
que tenga sentido.
Vea este ejemplo a continuación también:
String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
Se produce una excepción de puntero nulo cuando una aplicación intenta usar nulo en un caso donde se requiere un objeto. Éstos incluyen:
null
objeto.null
objeto.null
como si fuera una matriz.null
como si fuera una matriz.null
como si fuera un valor Lanzable.Las aplicaciones deben lanzar instancias de esta clase para indicar otros usos ilegales de null
objeto.
Referencia: http://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html
null
como objetivo de un synchronized
bloque, 2) usando a null
como objetivo de a switch
y unboxing null
.
Un null
puntero es aquel que apunta a ninguna parte. Cuando desreferencia un puntero p
, dices "dame los datos en la ubicación almacenada en" p ". Cuando p
es un null
puntero, la ubicación almacenada p
es 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, es porque algo no se ha inicializado correctamente.
NULL
está escrito como null
en java. Y es algo sensible a mayúsculas y minúsculas.
Ya hay muchas explicaciones para explicar cómo sucede y cómo solucionarlo, pero también debe seguir las mejores prácticas para evitar NullPointerException
s.
Ver también: una buena lista de mejores prácticas
Yo agregaría, muy importante, hacer un buen uso del final
modificador.
Usar el modificador "final" siempre que sea aplicable en Java
Resumen:
final
modificador para hacer cumplir una buena inicialización.@NotNull
y@Nullable
if("knownObject".equals(unknownObject)
valueOf()
sobre toString()
.StringUtils
métodos seguros nulos StringUtils.isEmpty(null)
.@Nullable
como se enumeran anteriormente) y advierten sobre posibles errores. También es posible inferir y generar tales anotaciones (por ejemplo, IntelliJ puede hacer eso) basándose en la estructura de código existente.
if (obj==null)
. Si es nulo, entonces debe escribir código para manejar eso también.
En Java, todo (excepto los tipos primitivos) tiene la forma de una clase.
Si desea utilizar cualquier objeto, entonces tiene dos fases:
Ejemplo:
Object object;
object = new Object();
Lo mismo para el concepto de matriz:
Item item[] = new Item[5];
item[0] = new Item();
Si no está dando la sección de inicialización, entonces NullPointerException
surja.
Una excepción de puntero nulo es un indicador de que está utilizando un objeto sin inicializarlo.
Por ejemplo, a continuación hay una clase de estudiante 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 ofrece 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á utilizando 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");
}
}
}
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 el método se ejecute. Luego, el tiempo de ejecución le permite saber esto lanzando una NullPointerException.
Su referencia está "apuntando" a nulo, por lo tanto, "Nulo -> Puntero".
El objeto vive en el espacio de memoria de VM y la única forma de acceder a él es utilizando this
referencias. 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 en 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...
Esta una cosa importante a saber - cuando no hay más referencias a un objeto (en el ejemplo anterior cuando reference
y otherReference
tanto punto a null) entonces el objeto es "inalcanzable". No hay forma de que podamos trabajar con él, por lo que este objeto está listo para ser recolectado, y en algún momento, la máquina virtual liberará la memoria utilizada por este objeto y asignará otro.
Otra ocurrencia de a NullPointerException
ocurre cuando uno declara una matriz de objetos, e inmediatamente trata de desreferenciar elementos dentro de ella.
String[] phrases = new String[10];
String keyPhrase = "Bird";
for(String phrase : phrases) {
System.out.println(phrase.equals(keyPhrase));
}
Este NPE particular puede evitarse si se invierte el orden de comparación; a saber, uso .equals
en 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 sonnull
.
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));
}
Optional
era volver nulo. La palabra clave está bien. Saber cómo protegerse es fundamental. Esto ofrece una ocurrencia común y formas de mitigarlo.