Cómo prevenir NullPointerExceptions en Java
Lucien Chemaly
21 de septiembre de 2023
0 minutos de lecturaComo desarrollador con experiencia en Java, probablemente conoces bien los matices de las excepciones de Java. Estos eventos indican condiciones anormales durante la ejecución del programa y, si no se manejan correctamente, pueden interrumpir el flujo normal de tu aplicación y provocar fallas o comportamientos inesperados.
Entre las distintas excepciones de Java, debes prestar especial atención a las NullPointerExceptions. Estas excepciones son particularmente conocidas por ser una causa frecuente de errores en tiempo de ejecución y pueden afectar considerablemente la estabilidad y confiabilidad de tu aplicación.
En este artículo aprenderás todo sobre las NullPointerExceptions en Java, incluidas sus causas, consecuencias y, sobre todo, cómo prevenirlas.
Por qué debes evitar las NullPointerExceptions
Una NullPointerException es una anomalía en tiempo de ejecución de Java que se produce cuando alguien intenta acceder a una referencia de objeto con valor null o modificarla. Esta referencia null indica que el objeto en cuestión no existe o no se ha instanciado. Cuando la máquina virtual de Java (JVM) encuentra una referencia null durante la manipulación de objetos, genera una NullPointerException. Comprender y abordar las causas de estas excepciones puede ayudarte a desarrollar aplicaciones más sólidas y robustas.
Las NullPointerExceptions pueden producirse en distintas situaciones, como las siguientes:
Al acceder a una variable de instancia o invocar un método de un objeto
null.Al acceder a un elemento de un arreglo o una colección mediante una referencia
null.Al desenvolver una referencia
nullde un objeto envoltorio para convertirla en el tipo primitivo correspondiente.Al pasar una referencia
nullcomo argumento a un método que no la admite.
Estas situaciones suelen deberse a errores de programación, como no inicializar un objeto, manejar incorrectamente los valores null o no comprobar debidamente si hay valores null antes de realizar operaciones con objetos.
Lamentablemente, las NullPointerExceptions pueden tener muchas consecuencias negativas en un entorno de desarrollo, entre ellas:
Bloqueos de la aplicación: Cuando se produce una
NullPointerException, a menudo la aplicación se cierra de forma abrupta, lo que causa insatisfacción en los usuarios y puede provocar la pérdida de datos no guardados.Comportamiento impredecible: Las
NullPointerExceptionspueden provocar un comportamiento impredecible de la aplicación, lo que dificulta reproducir y diagnosticar los problemas.Dificultades de depuración: Encontrar la causa raíz de una
NullPointerExceptionpuede llevar mucho tiempo y ser complicado, especialmente en bases de código grandes y complejas.Vulnerabilidades de seguridad: En algunos casos, las
NullPointerExceptionspueden revelar vulnerabilidades de seguridad, ya que usuarios maliciosos podrían aprovecharlas para evadir ciertas comprobaciones u obtener acceso no autorizado a datos confidenciales.Menor productividad de los desarrolladores: Las
NullPointerExceptionfrecuentes pueden obstaculizar la productividad, porque los desarrolladores deben dedicar más tiempo a corregir y probar estos problemas.
Por suerte, si comprendes las causas, consecuencias y técnicas de prevención de las NullPointerExceptions, puedes reducir su frecuencia y crear aplicaciones Java más estables, eficientes y seguras.
Cómo prevenir NullPointerExceptions en Java
Para prevenir las NullPointerExceptions en Java, los desarrolladores pueden aplicar diversas técnicas y prácticas recomendadas, como usar el operador ternario y la biblioteca Apache Commons StringUtils.
Usa el operador ternario
En lugar de usar una instrucción if-else, puedes usar el operador ternario para comprobar si hay valores null y proporcionar un valor predeterminado, con lo que reduces el riesgo de NullPointerExceptions. El operador ternario es más adecuado para expresiones condicionales concisas y legibles que manejan valores null y proporcionan valores predeterminados en Java:
Este código asigna el valor default a la variable result si la variable input es null. Si no es null, asigna el valor de input a result. Usa el operador ternario para comprobar si los valores son null y proporcionar un valor predeterminado.
Usa StringUtils de Apache Commons
La biblioteca Apache Commons StringUtils proporciona métodos de utilidad para manejar de forma segura valores null en cadenas, lo que reduce el riesgo de NullPointerException. Así se evitan bloqueos inesperados de la aplicación o vulnerabilidades de seguridad que pueden surgir a partir de valores null:
Este código usa la clase StringUtils del paquete org.apache.commons.lang3 para comprobar si una cadena es null, está vacía o solo contiene espacios en blanco, y asigna el resultado a la variable booleana isBlank.
Puedes reducir los posibles riesgos de seguridad de bibliotecas de terceros como Apache Commons StringUtils usando Snyk para analizar vulnerabilidades de Java. Además, usar la versión más reciente de las bibliotecas de terceros es fundamental para la seguridad y la estabilidad. Para Apache Commons StringUtils, consulta la versión más reciente en su sitio web oficial y actualiza la dependencia correspondiente en el archivo pom.xml de tu proyecto.
Prefiere los tipos primitivos a los objetos
Siempre que sea posible, usa tipos primitivos en lugar de sus clases envoltorio correspondientes para evitar posibles valores null, que no pueden ser null. Lamentablemente, sus clases envoltorio correspondientes sí pueden ser null, lo que puede provocar una NullPointerException si no se manejan correctamente:
Este código declara una variable entera primitiveInt con valor 0, que es de tipo primitivo (y, por lo tanto, no puede ser null), además de una variable objectInt de la clase Integer, que puede ser null y provocar una NullPointerException si no se maneja correctamente.
Evita devolver null en los métodos
En lugar de devolver valores null desde los métodos, devuelve un objeto vacío:
Este código define un método getStrings() que devuelve una lista vacía de cadenas mediante el método Collections.emptyList(), en lugar de devolver un valor null. Este enfoque es preferible porque evita posibles NullPointerExceptions y promueve la coherencia en el manejo de los valores de retorno, lo que facilita la lectura y el mantenimiento del código. Una forma útil de evitar devolver null desde métodos Java es usar Optional. Profundizaremos en este concepto más adelante.
Usa .equals() de forma segura
Usa .equals() en un objeto que sepas que no es null, o usa Objects.equals() para evitar NullPointerExceptions al comparar objetos:
Este código declara dos variables de cadena: str1 y str2. Usa el método .equals() en el objeto str1, que no es null, y Objects.equals() para comparar str1 y str2 de forma segura. Así comprueba si hay valores null para evitar posibles NullPointerExceptions al comparar objetos.
Evita pasar null como argumento a un método
Usa anotaciones como @NotNull para indicar que un método no debe aceptar argumentos null, lo que permite realizar comprobaciones en tiempo de compilación:
Usa la anotación @NonNull de Lombok
Lombok es una biblioteca muy usada que simplifica el código Java. La anotación @NonNull ayuda a exigir parámetros no nulos y genera las comprobaciones null correspondientes:
Este código importa la anotación @NonNull de la biblioteca Lombok y la usa para especificar que el parámetro input del método doSomething no debe ser null. Esto ayuda a prevenir posibles NullPointerExceptions al comprobar si hay valores null en tiempo de compilación.
Usa el patrón de objeto null
El patrón de objeto null es un patrón de diseño que proporciona un objeto predeterminado en lugar de valores null, lo que reduce el riesgo de NullPointerExceptions:
Este código define una interfaz Animal y dos clases que la implementan: Dog y NullAnimal. La clase AnimalFactory contiene un método, getAnimal(), que recibe un argumento String y devuelve una instancia de Dog o NullAnimal, según el argumento.
Este enfoque es útil para manejar valores null, ya que permite crear un objeto predeterminado NullAnimal si se pasa un argumento no válido o null a getAnimal(). Esto puede prevenir posibles NullPointerExceptions y garantizar un comportamiento coherente en el programa.
Usa la API Stream de Java para manejar valores null en colecciones
La API Stream de Java proporciona métodos para manejar de forma segura valores null en colecciones:
Este código usa un stream para filtrar los elementos null de una lista de cadenas, con lo que previene posibles NullPointerExceptions que pueden producirse al intentar acceder a valores null de la lista.
Este enfoque simplifica la comprobación de valores null y ayuda a prevenir bloqueos inesperados de la aplicación o vulnerabilidades de seguridad que pueden surgir a partir de valores null en las colecciones.
Valida los argumentos de los métodos con un método de utilidad específico
Crea un método de utilidad para validar argumentos y lanzar una excepción personalizada cuando se encuentren valores null. Esto puede ayudar a exigir que los parámetros de los métodos no sean nulos:
Este código define un método checkNotNull que lanza una IllegalArgumentException si el parámetro reference es null, y un método doSomething que usa checkNotNull para asegurarse de que el parámetro input no sea null antes de continuar con la implementación del método.
Este enfoque es importante para prevenir posibles NullPointerExceptions, ya que comprueba si hay valores null en tiempo de ejecución y lanza una excepción si encuentra alguno null.
Usa métodos predeterminados en las interfaces
Al usar interfaces, proporciona métodos predeterminados para manejar posibles objetos null y evitar así las NullPointerExceptions:
Este código define una interfaz Processor con un método predeterminado process() que comprueba si los valores de entrada son null y llama a doProcess() o handleNullInput(). La clase PMyProcessor implementa la interfaz Processor y sobrescribe los métodos doProcess() y handleNullInput() para definir un comportamiento personalizado.
Este enfoque ayuda a prevenir posibles NullPointerExceptions al ofrecer una forma coherente de manejar valores de entrada null, lo que permite implementar un comportamiento personalizado o gestionar excepciones cuando se encuentran valores null.
Usa el patrón Builder
El patrón Builder puede ayudarte a crear objetos complejos con parámetros opcionales (por ejemplo, el color, el tamaño, la forma u otras características personalizables de un objeto) y garantizar que los parámetros obligatorios siempre estén definidos y no sean nulos:
Este código define una clase Person con un constructor privado que solo acepta un objeto Builder, que contiene dos campos: firstName y lastName. La clase Builder garantiza que el campo firstName no sea null mediante Objects.requireNonNull() para comprobarlo durante la construcción. El campo lastName puede ser null, pero debe comprobarse y establecerse mediante el método lastName().
Este enfoque ayuda a prevenir posibles NullPointerExceptions al garantizar que todos los campos obligatorios se inicialicen y se comprueben para detectar valores null durante la construcción. Esto promueve la coherencia y la confiabilidad del programa.
Usa el patrón de bloqueo con doble verificación junto con el patrón singleton
Al crear singletons, el patrón de bloqueo con doble verificación puede ayudar a garantizar que la instancia de singleton nunca sea null:
Este código define una clase Singleton que garantiza que solo se cree una instancia de la clase durante la ejecución y usa el bloqueo con doble verificación para evitar posibles condiciones de carrera y NullPointerExceptions.
Este enfoque es importante para prevenir NullPointerExceptions, ya que garantiza que la instancia de la clase Singleton se cree una sola vez y que las llamadas posteriores al método getInstance() devuelvan la misma instancia. Esto ayuda a evitar que se asignen accidentalmente valores null a la variable instance y que se produzcan posibles NullPointerExceptions.
Usa la clase Optional
La clase Optional es una herramienta eficaz para evitar NullPointerExceptions mediante el manejo explícito de posibles valores null:
Este código usa la clase Optional para manejar valores null, indica explícitamente que un valor puede estar ausente y usa orElse() para proporcionar un valor predeterminado, lo que evita posibles NullPointerExceptions. Es importante no llamar directamente a get() en Optional. En su lugar, se usan métodos como orElse() para proporcionar un valor predeterminado cuando Optional está vacío y así evitar NoSuchElementException.
Este enfoque no solo deja claro que un valor puede estar presente o no, sino que también garantiza que estos casos se manejen correctamente, lo que ayuda a evitar posibles errores como NullPointerException o NoSuchElementException.
Haz pruebas con frecuencia y desde el inicio
Asegúrate de escribir pruebas unitarias exhaustivas para detectar NullPointerExceptions durante el desarrollo, antes de que causen problemas en producción:
Este código define un caso de prueba de JUnit para el método getStrings() de la clase MyClass. Luego crea una instancia de MyClass, llama al método getStrings() y afirma que el resultado no es null mediante el método assertNotNull().
Este enfoque ayuda a prevenir posibles NullPointerExceptions, ya que verifica que el resultado del método getStrings() sea not null antes de realizar otras operaciones. Al garantizar que el resultado sea not null, el caso de prueba puede ayudar a detectar posibles problemas con los valores null y evitar fallas inesperadas de la aplicación o vulnerabilidades de seguridad.
Conclusión sobre NullPoint
Prevenir NullPointerExceptions en las aplicaciones Java es fundamental para crear software sólido, confiable y seguro. A partir de Java 14, se incorporaron mejoras para manejar mejor las NullPointerException, lo que facilita prevenir y diagnosticar estas excepciones. Por eso, actualizar a versiones más recientes de Java puede ser beneficioso para la depuración.
Al combinar varias técnicas, como usar el operador ternario, validar los argumentos de los métodos y adoptar patrones de diseño como Builder y el patrón de objeto nulo, puedes reducir eficazmente el riesgo de encontrar NullPointerExceptions. Incorporar estas prácticas no solo ayuda a mejorar la estabilidad de la aplicación, sino también la experiencia general de desarrollo.
Snyk es una herramienta esencial para los desarrolladores que buscan crear aplicaciones seguras y confiables. Con Snyk, tienes acceso a una variedad de herramientas y servicios diseñados para detectar y corregir vulnerabilidades de seguridad en tu código. Snyk también se integra con entornos y plataformas de desarrollo populares, lo que facilita incorporar la seguridad a tu proceso de desarrollo.
Para llevar tu experiencia de desarrollo en Java al siguiente nivel y contribuir a la seguridad y estabilidad del ecosistema de software, considera usar algunas de las potentes herramientas de Snyk, como los complementos de IDE, la CLI y las lecciones prácticas gratuitas sobre seguridad en Java de Snyk. Estas herramientas pueden ayudarte a detectar e informar vulnerabilidades de seguridad, incluidas las NullPointerExceptions, para que tus aplicaciones sean seguras y confiables.
Encuentra y corrige vulnerabilidades en Java
Protege tus aplicaciones con el análisis de vulnerabilidades de Snyk y recomendaciones para corregirlas.
No necesitas tarjeta de crédito.
O regístrate con Azure AD Docker ID Bitbucket
Al usar Snyk, aceptas cumplir nuestras políticas, incluidos nuestros Términos del servicio y nuestra Política de privacidad.



