Java NullPointerException – Detectar, Corregir y Mejores Prácticas

java.lang.NullPointerException es una de las excepciones más populares en java programación. Cualquiera que trabaje en java debe haber visto cómo aparece de la nada en el programa independiente de java, así como en la aplicación web de java.

java.lang.NullPointerException

NullPointerException es una excepción en tiempo de ejecución, por lo que no necesitamos atraparla en el programa. NullPointerException se genera en una aplicación cuando intentamos realizar alguna operación en null donde se requiere un objeto. Algunas de las razones comunes para NullPointerException en programas java son:

  1. Invocar un método en una instancia de objeto pero en tiempo de ejecución el objeto es null.
  2. Acceder a variables de una instancia de objeto que es null en tiempo de ejecución.
  3. Lanzar null en el programa
  4. Acceder al índice o modificar el valor de un índice de un array que es null
  5. Comprobar la longitud de un array que es null en tiempo de ejecución.

Ejemplos de NullPointerException en Java

Veamos algunos ejemplos de NullPointerException en programas de Java.

1. NullPointerException al llamar a un método de instancia

public class Temp {

	public static void main(String[] args) {

		Temp t = initT();
		
		t.foo("Hi");
		
	}

	private static Temp initT() {
		return null;
	}

	public void foo(String s) {
		System.out.println(s.toLowerCase());
	}
}

Cuando ejecutamos el programa anterior, lanza el siguiente mensaje de error de NullPointerException.

Exception in thread "main" java.lang.NullPointerException
	at Temp.main(Temp.java:7)

Obtenemos NullPointerException en la declaración t.foo("Hola"); porque “t” es nulo aquí.

2. Java NullPointerException al acceder/modificar un campo de un objeto nulo

public class Temp {

	public int x = 10;
	
	public static void main(String[] args) {

		Temp t = initT();
		
		int i = t.x;
		
	}

	private static Temp initT() {
		return null;
	}

}

El programa anterior lanza la siguiente traza de NullPointerException.

Exception in thread "main" java.lang.NullPointerException
	at Temp.main(Temp.java:9)

Se está lanzando NullPointerException en la declaración int i = t.x; porque “t” es nulo aquí.

3. Java NullPointerException cuando se pasa null como argumento de método

public class Temp {

	public static void main(String[] args) {

		foo(null);

	}

	public static void foo(String s) {
		System.out.println(s.toLowerCase());
	}
}

Este es uno de los casos más comunes de java.lang.NullPointerException porque es el llamador quien está pasando el argumento nulo.

La siguiente imagen muestra la excepción de puntero nulo cuando se ejecuta el programa anterior en Eclipse IDE.

4. java.lang.NullPointerException cuando se lanza null

public class Temp {

	public static void main(String[] args) {

		throw null;
	}

}

A continuación se muestra la traza de la pila de excepciones del programa anterior, que muestra NullPointerException debido a la instrucción throw null;.

Exception in thread "main" java.lang.NullPointerException
	at Temp.main(Temp.java:5)

5. NullPointerException al obtener la longitud de un array nulo

public class Temp {

	public static void main(String[] args) {

		int[] data = null;
		
		int len = data.length;
	}

}
Exception in thread "main" java.lang.NullPointerException
	at Temp.main(Temp.java:7)

6. NullPointerException al acceder al valor del índice de un array nulo

public class Temp {

	public static void main(String[] args) {

		int[] data = null;
		
		int len = data[2];
	}

}
Exception in thread "main" java.lang.NullPointerException
	at Temp.main(Temp.java:7)

7. Excepción de NullPointerException al sincronizar en un objeto nulo

public class Temp {

	public static String mutex = null;
	
	public static void main(String[] args) {

		synchronized(mutex) {
			System.out.println("synchronized block");
		}		
	}
}

La instrucción synchronized(mutex) lanzará una NullPointerException porque el objeto “mutex” es nulo.

8. Estado HTTP 500 java.lang.NullPointerException

En ocasiones, obtenemos una respuesta de página de error desde una aplicación web Java con un mensaje de error que dice “Estado HTTP 500 – Error interno del servidor” y la causa principal es java.lang.NullPointerException.

Para solucionar esto, edité el proyecto de ejemplo de Spring MVC y modifiqué el método HomeController de la siguiente manera.

@RequestMapping(value = "/user", method = RequestMethod.POST)
	public String user(@Validated User user, Model model) {
		System.out.println("User Page Requested");
		System.out.println("User Name: "+user.getUserName().toLowerCase());
		System.out.println("User ID: "+user.getUserId().toLowerCase());
		model.addAttribute("userName", user.getUserName());
		return "user";
	}

La siguiente imagen muestra el mensaje de error generado por la respuesta de la aplicación web.

Aquí está la traza de la excepción:

HTTP Status 500Internal Server Error

Type Exception Report

Message Request processing failed; nested exception is java.lang.NullPointerException

Description The server encountered an unexpected condition that prevented it from fulfilling the request.

Exception

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException
	org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
	org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
	org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
Root Cause

java.lang.NullPointerException
	com.journaldev.spring.controller.HomeController.user(HomeController.java:38)
	sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	java.lang.reflect.Method.invoke(Method.java:498)

La causa principal es una NullPointerException en la instrucción user.getUserId().toLowerCase() porque user.getUserId() devuelve nulo.

Cómo detectar java.lang.NullPointerException

La detección de NullPointerException es muy fácil, simplemente observa la traza de la excepción y te mostrará el nombre de la clase y el número de línea de la excepción. Luego, mira el código y ve qué podría ser nulo causando la NullPointerException. Simplemente mira todos los ejemplos anteriores, es muy claro desde la traza de la pila qué está causando la excepción de puntero nulo.

Cómo solucionar NullPointerException

java.lang.NullPointerException es una excepción no verificada, por lo que no tenemos que capturarla. Las excepciones de puntero nulo pueden prevenirse mediante comprobaciones nulas y técnicas de codificación preventiva. Mira los siguientes ejemplos de código que muestran cómo evitar java.lang.NullPointerException.

if(mutex ==null) mutex =""; // codificación preventiva
		
synchronized(mutex) {
	System.out.println("synchronized block");
}
// usando comprobaciones nulas
if(user!=null && user.getUserName() !=null) {
System.out.println("User Name: "+user.getUserName().toLowerCase());
}
if(user!=null && user.getUserName() !=null) {
	System.out.println("User ID: "+user.getUserId().toLowerCase());
}

Prácticas recomendadas de codificación para evitar NullPointerException

1. Consideremos la siguiente función y estemos atentos a escenarios que puedan causar una excepción de puntero nulo.

public void foo(String s) {
    if(s.equals("Test")) {
	System.out.println("test");
    }
}

La NullPointerException puede ocurrir si se pasa el argumento como nulo. El mismo método puede escribirse de la siguiente manera para evitar la NullPointerException.

public void foo(String s) {
	if ("Test".equals(s)) {
		System.out.println("test");
	}
}

2. También podemos agregar una verificación de nulo para el argumento y lanzar IllegalArgumentException si es necesario.

public int getArrayLength(Object[] array) {
	
	if(array == null) throw new IllegalArgumentException("array is null");
	
	return array.length;
}

3. Podemos usar el operador ternario como se muestra en el siguiente ejemplo de código.

String msg = (str == null) ? "" : str.substring(0, str.length()-1);

4. Use String.valueOf() en lugar del método toString(). Por ejemplo, revisa el código del método println() de PrintStream que está definido de la siguiente manera.

public void println(Object x) {
        String s = String.valueOf(x);
        synchronized (this) {
            print(s);
            newLine();
        }
    }

El fragmento de código a continuación muestra un ejemplo donde se utiliza el método valueOf() en lugar de toString().

Object mutex = null;

// imprime null
System.out.println(String.valueOf(mutex));

// lanzará java.lang.NullPointerException
System.out.println(mutex.toString());

5. Escribe métodos que devuelvan objetos vacíos en lugar de nulos siempre que sea posible, por ejemplo, lista vacía, cadena vacía, etc.

6. Hay algunos métodos definidos en las clases de colección para evitar NullPointerException, deberías usarlos. Por ejemplo, contains(), containsKey(), y containsValue().

Referencia: Documento de API

Source:
https://www.digitalocean.com/community/tutorials/java-lang-nullpointerexception