Como evitar NullPointerExceptions em Java
Lucien Chemaly
21 de setembro de 2023
0 minutos de leituraComo desenvolvedor Java experiente, você provavelmente conhece bem as particularidades das exceções em Java. Esses eventos sinalizam condições anormais durante a execução do programa e, quando não são tratados corretamente, podem interromper o fluxo normal da aplicação, causando falhas ou comportamentos inesperados.
Entre as várias exceções do Java, é importante prestar atenção especial a NullPointerExceptions. Elas são particularmente conhecidas por serem uma causa comum de erros em tempo de execução e podem afetar significativamente a estabilidade e a confiabilidade da sua aplicação.
Neste artigo, você vai aprender tudo sobre NullPointerExceptions em Java: suas causas, consequências e, principalmente, como evitá-las.
Por que você deve evitar NullPointerExceptions
Uma NullPointerException é uma anomalia em tempo de execução no Java que ocorre quando alguém tenta acessar ou modificar uma referência de objeto com valor null. Essa referência null indica que o objeto em questão não existe ou não foi instanciado. Quando a máquina virtual Java (JVM) encontra uma referência null durante a manipulação de objetos, ela gera uma NullPointerException. Entender e tratar as causas dessas exceções ajuda a criar aplicações mais sólidas e robustas.
As NullPointerExceptions podem ocorrer em várias situações, como:
Ao acessar uma variável de instância ou chamar um método de um objeto
null.Ao acessar um elemento de um array ou de uma coleção por meio de uma referência
null.Ao converter uma referência
nullde um objeto wrapper para o tipo primitivo correspondente.Ao passar uma referência
nullcomo argumento para um método que não espera recebê-la.
Essas situações geralmente resultam de erros de programação, como não inicializar um objeto, lidar incorretamente com valores null ou não verificar se há valores null antes de operar com objetos.
Infelizmente, as NullPointerExceptions podem trazer várias consequências negativas para o ambiente de desenvolvimento, como:
Falhas na aplicação: quando ocorre uma
NullPointerException, muitas vezes a aplicação é encerrada abruptamente, causando insatisfação aos usuários e podendo levar à perda de dados não salvos.Comportamento imprevisível: as
NullPointerExceptionspodem causar um comportamento imprevisível na aplicação, dificultando a reprodução e o diagnóstico dos problemas.Dificuldades na depuração: encontrar a causa raiz de uma
NullPointerExceptionpode ser demorado e complexo, especialmente em bases de código grandes e complexas.Vulnerabilidades de segurança: em alguns casos, as
NullPointerExceptionspodem expor vulnerabilidades de segurança, pois usuários mal-intencionados podem explorá-las para contornar certas verificações ou obter acesso não autorizado a dados confidenciais.Queda na produtividade dos desenvolvedores: a ocorrência frequente de
NullPointerExceptionpode prejudicar a produtividade, pois os desenvolvedores precisam dedicar mais tempo a corrigir e testar esses problemas.
Felizmente, ao entender as causas, as consequências e as formas de evitar NullPointerExceptions, você pode reduzir sua ocorrência e criar aplicações Java mais estáveis, eficientes e seguras.
Como evitar NullPointerExceptions em Java
Para evitar NullPointerExceptions em Java, os desenvolvedores podem adotar diversas técnicas e boas práticas, como o uso do operador ternário e da biblioteca Apache Commons StringUtils.
Use o operador ternário
Em vez de usar uma instrução if-else, você pode usar o operador ternário para verificar valores null e fornecer um valor padrão, reduzindo o risco de NullPointerExceptions. O operador ternário é uma opção mais concisa e legível para instruções condicionais que tratam valores null e fornecem valores padrão em Java:
Este código atribui o valor default à variável result se a variável input for null. Se não for null, atribui o valor de input a result. O operador ternário verifica se há valores null e fornece um valor padrão.
Use StringUtils do Apache Commons
A biblioteca Apache Commons StringUtils oferece métodos utilitários para lidar com segurança com valores null em strings, reduzindo o risco de NullPointerException. Isso ajuda a evitar falhas inesperadas na aplicação ou vulnerabilidades de segurança causadas por valores null:
Este código usa a classe StringUtils do pacote org.apache.commons.lang3 para verificar se uma string é null, está vazia ou contém apenas espaços em branco, e atribui o resultado à variável booleana isBlank.
Você pode reduzir os possíveis riscos de segurança de bibliotecas de terceiros, como Apache Commons StringUtils, usando o Snyk para detectar vulnerabilidades em Java. Além disso, é essencial usar a versão mais recente das bibliotecas de terceiros para manter a segurança e a estabilidade. No caso do Apache Commons StringUtils, confira a versão mais recente no site oficial e atualize-a no arquivo pom.xml do seu projeto.
Prefira tipos primitivos a objetos
Sempre que possível, use tipos primitivos em vez das respectivas classes wrapper para evitar valores null, que não podem ser null. Já as classes wrapper correspondentes podem ser null, o que pode causar uma NullPointerException se não forem tratadas corretamente:
Este código declara a variável inteira primitiveInt com valor 0, que é primitivo (e, portanto, não pode ser null), além da variável objectInt da classe Integer, que pode ser null e causar uma NullPointerException se não for tratada corretamente.
Evite retornar null em métodos
Em vez de retornar valores null nos métodos, retorne um objeto vazio:
Este código define o método getStrings(), que retorna uma lista vazia de strings usando o método Collections.emptyList(), em vez de retornar um valor null. Essa abordagem é preferível porque evita possíveis NullPointerExceptions e promove consistência no tratamento de valores de retorno, facilitando a leitura e a manutenção do código. Uma abordagem útil para evitar retornos null em métodos Java é usar Optional. Vamos explorar esse conceito mais adiante.
Use .equals() com segurança
Use .equals() em um objeto que você sabe que não é nulo ou use Objects.equals() para evitar NullPointerExceptions ao comparar objetos:
Este código declara duas variáveis de string: str1 e str2. Ele usa o método .equals() no objeto não nulo str1 e Objects.equals() para comparar str1 e str2 com segurança, verificando valores null para evitar possíveis NullPointerExceptions na comparação.
Evite passar null como argumento de método
Use anotações como @NotNull para indicar que um método não deve aceitar argumentos null, permitindo verificações em tempo de compilação:
Use a anotação @NonNull do Lombok
Lombok é uma biblioteca bastante usada que simplifica o código Java. A anotação @NonNull ajuda a garantir que os parâmetros não sejam nulos, gerando verificações apropriadas de null:
Este código importa a anotação @NonNull da biblioteca Lombok e a usa para especificar que o parâmetro input do método doSomething não pode ser null. Isso ajuda a evitar possíveis NullPointerExceptions ao verificar valores null em tempo de compilação.
Use o padrão de objeto null
O padrão de objeto null é um padrão de projeto que fornece um objeto padrão no lugar de valores null, reduzindo o risco de NullPointerExceptions:
Este código define uma interface Animal e duas classes que a implementam: Dog e NullAnimal. A classe AnimalFactory contém um método, getAnimal(), que recebe um argumento String e retorna uma instância de Dog ou NullAnimal, dependendo do argumento.
Essa abordagem é útil para lidar com valores null, pois permite criar um objeto NullAnimal padrão caso um argumento inválido ou null seja passado para getAnimal(). Isso pode evitar possíveis NullPointerExceptions e garantir um comportamento consistente no programa.
Use a API Stream do Java para tratar valores null em coleções
A API Stream do Java oferece métodos para lidar com segurança com valores null em coleções:
Este código usa um stream para filtrar elementos null de uma lista de strings, evitando possíveis NullPointerExceptions que podem ocorrer ao tentar acessar valores null na lista.
Essa abordagem simplifica a verificação de valores null e ajuda a evitar falhas inesperadas na aplicação ou vulnerabilidades de segurança que podem surgir de valores null em coleções.
Valide argumentos de métodos com um método utilitário específico
Crie um método utilitário para validar argumentos e lançar uma exceção personalizada quando encontrar valores null. Isso pode ajudar a garantir que os parâmetros de métodos não sejam nulos:
Este código define o método checkNotNull, que lança uma IllegalArgumentException se o parâmetro reference for null, e o método doSomething, que usa checkNotNull para garantir que o parâmetro input não seja null antes de continuar a implementação do método.
Essa abordagem é importante para evitar possíveis NullPointerExceptions, pois verifica valores null em tempo de execução e lança uma exceção caso encontre valores null.
Use métodos padrão em interfaces
Ao usar interfaces, forneça métodos padrão para lidar com possíveis objetos null e, assim, evitar NullPointerExceptions:
Este código define a interface Processor com um método padrão process(), que verifica se os valores de entrada são null e chama doProcess() ou handleNullInput(). A classe PMyProcessor implementa a interface Processor e sobrescreve os métodos doProcess() e handleNullInput() para definir um comportamento personalizado.
Essa abordagem ajuda a evitar possíveis NullPointerExceptions, oferecendo uma maneira consistente de lidar com valores de entrada null e permitindo implementar um comportamento personalizado ou o tratamento de exceções quando valores null forem encontrados.
Use o padrão Builder
O padrão Builder ajuda você a criar objetos complexos com parâmetros opcionais (como cor, tamanho, formato ou outros recursos personalizáveis), garantindo que os parâmetros obrigatórios sejam sempre definidos e não sejam nulos:
Este código define a classe Person com um construtor privado que aceita apenas um objeto Builder, que contém dois campos: firstName e lastName. A classe Builder garante que o campo firstName não seja null, usando Objects.requireNonNull() para verificá-lo durante a construção. Já o campo lastName pode ser null, mas deve ser verificado e definido usando o método lastName().
Essa abordagem ajuda a evitar possíveis NullPointerExceptions, garantindo que todos os campos obrigatórios sejam inicializados e verificados quanto a valores null durante a construção. Isso promove consistência e confiabilidade no programa.
Use o padrão de bloqueio com verificação dupla junto com o padrão singleton
Ao criar singletons, o padrão de bloqueio com verificação dupla pode ajudar a garantir que a instância do singleton nunca seja null:
Este código define uma classe Singleton que garante a criação de apenas uma instância da classe durante a execução e usa o bloqueio com verificação dupla para evitar possíveis condições de corrida e NullPointerExceptions.
Essa abordagem é importante para evitar NullPointerExceptions, pois garante que a instância da classe Singleton seja criada apenas uma vez e que as chamadas seguintes ao método getInstance() retornem a mesma instância. Isso ajuda a evitar que valores null sejam atribuídos acidentalmente à variável instance, causando possíveis NullPointerExceptions.
Use a classe Optional
A classe Optional é uma ferramenta poderosa para evitar NullPointerExceptions, tratando explicitamente possíveis valores null:
Este código usa a classe Optional para tratar valores null, indicando explicitamente que um valor pode estar ausente e usando orElse() para fornecer um valor padrão e, assim, evitar possíveis NullPointerExceptions. É importante não chamar get() diretamente em Optional. Em vez disso, use métodos como orElse() para fornecer um valor padrão quando o Optional estiver vazio, evitando assim a NoSuchElementException.
Essa abordagem não só deixa claro que um valor pode estar presente ou não, como também garante o tratamento correto desses casos, ajudando a evitar erros como NullPointerException ou NoSuchElementException.
Teste com frequência e desde o início
Escreva testes de unidade abrangentes para detectar NullPointerExceptions durante o desenvolvimento, antes que causem problemas em produção:
Este código define um caso de teste JUnit para o método getStrings() da classe MyClass. Em seguida, cria uma instância de MyClass, chama o método getStrings() e verifica se o resultado não é null usando o método assertNotNull().
Essa abordagem ajuda a evitar possíveis NullPointerExceptions, verificando se o resultado do método getStrings() é not null antes de realizar outras operações com ele. Ao garantir que o resultado seja not null, o caso de teste pode ajudar a identificar possíveis problemas com valores null e evitar falhas inesperadas no aplicativo ou vulnerabilidades de segurança.
Conclusão sobre NullPointer
Evitar NullPointerExceptions em aplicativos Java é essencial para criar software robusto, confiável e seguro. A partir do Java 14, foram implementadas melhorias para facilitar o tratamento de NullPointerException, tornando mais fácil evitar e investigar essas exceções. Por isso, atualizar para versões mais recentes do Java pode ajudar na depuração.
Combinando técnicas como o uso do operador ternário, a validação de argumentos de métodos e a adoção de padrões de projeto, como Builder e objeto nulo, você pode reduzir com eficiência o risco de encontrar NullPointerExceptions. Essas práticas ajudam a melhorar a estabilidade do aplicativo e também aprimoram a experiência geral de desenvolvimento.
Snyk é uma ferramenta essencial para desenvolvedores que querem criar aplicativos seguros e confiáveis. Com a Snyk, você tem acesso a várias ferramentas e serviços criados para detectar e corrigir vulnerabilidades de segurança no seu código. A Snyk também se integra a ambientes e plataformas de desenvolvimento populares, facilitando a inclusão da segurança no seu processo de desenvolvimento.
Para levar sua experiência de desenvolvimento Java a outro nível e contribuir para a segurança e a estabilidade do ecossistema de software, considere usar algumas das poderosas ferramentas da Snyk, como os plug-ins de IDE, a CLI e as lições gratuitas e práticas de segurança em Java da Snyk. Essas ferramentas ajudam você a detectar e reportar vulnerabilidades de segurança, incluindo NullPointerExceptions, garantindo que seus aplicativos sejam seguros e confiáveis.
Encontre e corrija vulnerabilidades em Java
Proteja suas aplicações com a análise de vulnerabilidades e as recomendações de correção da Snyk.
Não é necessário cartão de crédito.
Ou cadastre-se com Azure AD Docker ID Bitbucket
Ao usar a Snyk, você concorda em cumprir nossas políticas, incluindo os Termos de Serviço e a Política de Privacidade.



