Java NullPointerException – Detectar, Corrigir e Melhores Práticas

java.lang.NullPointerException é uma das exceções mais populares em java programação. Qualquer pessoa que trabalhe em java deve ter visto isso surgir do nada no programa standalone java, bem como no aplicativo web java.

java.lang.NullPointerException

NullPointerException é uma exceção de tempo de execução, então não precisamos capturá-la no programa. NullPointerException é gerado em um aplicativo quando estamos tentando fazer alguma operação em nulo onde um objeto é necessário. Algumas das razões comuns para NullPointerException em programas java são:

  1. Invocar um método em uma instância de objeto mas em tempo de execução o objeto é nulo.
  2. Acessar variáveis de uma instância de objeto que está nula em tempo de execução.
  3. Lançando nulo no programa
  4. Acessar índice ou modificar valor de um índice de um array que está nulo
  5. Verificar o comprimento de um array que está nulo em tempo de execução.

Exemplos de Java NullPointerException

Vamos analisar alguns exemplos de NullPointerException em programas Java.

1. NullPointerException ao chamar um método de instância

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

Ao executarmos o programa acima, ele lança a seguinte mensagem de erro NullPointerException.

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

Estamos obtendo NullPointerException na instrução t.foo("Hi"); porque “t” é nulo aqui.

2. Java NullPointerException ao acessar/modificar o campo de um 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;
	}

}

O programa acima gera o seguinte rastreamento de pilha NullPointerException.

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

NullPointerException está sendo lançada na instrução int i = t.x; porque “t” é nulo aqui.

3. Java NullPointerException quando null é passado 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 é um dos casos mais comuns de java.lang.NullPointerException porque é o chamador quem está passando o argumento nulo.

A imagem abaixo mostra a exceção de ponteiro nulo quando o programa acima é executado no Eclipse IDE.

4. java.lang.NullPointerException quando null é lançado

public class Temp {

	public static void main(String[] args) {

		throw null;
	}

}

Abaixo está a rastreabilidade de exceção do programa acima, mostrando NullPointerException devido à instrução throw null;.

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

5. NullPointerException ao obter o comprimento de uma matriz nula

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 ao acessar o valor do índice de uma matriz nula

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. NullPointerException ao sincronizar em um objeto nulo

public class Temp {

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

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

O synchronized(mutex) lançará NullPointerException porque o objeto “mutex” é nulo.

8. Status HTTP 500 java.lang.NullPointerException

Às vezes, obtemos uma resposta de página de erro de uma aplicação web Java com uma mensagem de erro como “Status HTTP 500 – Erro interno do servidor” e causa raiz como java.lang.NullPointerException.

Para isso, editei o projeto Exemplo Spring MVC e alterei o método HomeController conforme abaixo.

@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";
	}

A imagem abaixo mostra a mensagem de erro lançada pela resposta da aplicação web.

Aqui está o rastreamento de pilha de exceção:

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)

A causa raiz é NullPointerException na declaração user.getUserId().toLowerCase() porque user.getUserId() está retornando nulo.

Como detectar java.lang.NullPointerException

Detectar NullPointerException é muito fácil, basta olhar para o rastreamento da exceção e ele mostrará o nome da classe e o número da linha da exceção. Em seguida, examine o código e veja o que poderia ser nulo causando a NullPointerException. Basta olhar todos os exemplos acima, é muito claro a partir do rastreamento de pilha o que está causando a exceção de ponteiro nulo.

Como corrigir NullPointerException

java.lang.NullPointerException é uma exceção não verificada, então não precisamos capturá-la. As exceções de ponteiro nulo podem ser prevenidas usando verificações nulas e técnicas de codificação preventiva. Veja abaixo exemplos de código que mostram como evitar java.lang.NullPointerException.

if(mutex ==null) mutex =""; //codificação preventiva
		
synchronized(mutex) {
	System.out.println("synchronized block");
}
//usando verificações 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áticas recomendadas de codificação para evitar NullPointerException

1. Vamos considerar a função abaixo e procurar por cenários que possam causar uma exceção de ponteiro nulo.

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

A NullPointerException pode ocorrer se o argumento estiver sendo passado como nulo. O mesmo método pode ser escrito da seguinte forma para evitar NullPointerException.

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

2. Também podemos adicionar uma verificação nula para o argumento e lançar IllegalArgumentException se necessário.

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

3. Podemos usar o operador ternário como mostrado no código de exemplo abaixo.

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

4. Use String.valueOf() em vez do método toString(). Por exemplo, verifique que o código do método println() da classe PrintStream está definido como abaixo.

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

O trecho de código abaixo mostra o exemplo em que o método valueOf() é usado em vez de toString().

Object mutex = null;

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

// lançará java.lang.NullPointerException
System.out.println(mutex.toString());

5. Escreva métodos que retornem objetos vazios em vez de nulo sempre que possível, por exemplo, lista vazia, string vazia, etc.

6. Existem alguns métodos definidos em classes de coleção para evitar NullPointerException, você deve usá-los. Por exemplo, contains(), containsKey() e containsValue().

Referência: Documento de API

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