Introducción
Una excepción es un evento de error que puede ocurrir durante la ejecución de un programa y perturbar su flujo normal. Java proporciona una forma robusta y orientada a objetos de manejar escenarios de excepción conocida como Manejo de Excepciones en Java.
Las excepciones en Java pueden surgir de diferentes tipos de situaciones, como datos incorrectos ingresados por el usuario, falla de hardware, falla de conexión de red o un servidor de base de datos que está caído. El código que especifica qué hacer en escenarios de excepción específicos se llama manejo de excepciones.
Lanzamiento y Captura de Excepciones
Java crea un objeto de excepción cuando ocurre un error durante la ejecución de una declaración. El objeto de excepción contiene mucha información de depuración, como la jerarquía de métodos, el número de línea donde ocurrió la excepción y el tipo de excepción.
Si ocurre una excepción en un método, el proceso de crear el objeto de excepción y entregarlo al entorno de ejecución se llama “lanzar la excepción”. El flujo normal del programa se detiene y el Entorno de Ejecución de Java (JRE) intenta encontrar el controlador para la excepción. El controlador de excepciones es el bloque de código que puede procesar el objeto de excepción.
- La lógica para encontrar el controlador de excepciones comienza con la búsqueda en el método donde ocurrió el error.
- Si no se encuentra un controlador apropiado, se moverá al método llamador.
- Y así sucesivamente.
Entonces, si la pila de llamadas del método es A->B->C
y se produce una excepción en el método C
, la búsqueda del controlador apropiado se moverá de C->B->A
.
Si se encuentra un controlador de excepciones adecuado, se pasa el objeto de excepción al controlador para procesarlo. Se dice que el controlador está “capturando la excepción”. Si no hay un controlador de excepciones adecuado, el programa termina e imprime información sobre la excepción en la consola.
El marco de manejo de excepciones de Java se utiliza para manejar solo errores en tiempo de ejecución. Los errores en tiempo de compilación deben corregirse por el desarrollador que escribe el código, de lo contrario, el programa no se ejecutará.
Palabras clave de manejo de excepciones en Java
Java proporciona palabras clave específicas para fines de manejo de excepciones.
- throw – Sabemos que si ocurre un error, se crea un objeto de excepción y luego Java inicia el procesamiento para manejarlo. A veces, es posible que deseemos generar excepciones explícitamente en nuestro código. Por ejemplo, en un programa de autenticación de usuario, deberíamos lanzar excepciones a los clientes si la contraseña es
null
. La palabra clavethrow
se utiliza para lanzar excepciones al tiempo de ejecución de Java para manejarlas. - throws – Cuando estamos lanzando una excepción en un método y no la estamos manejando, entonces tenemos que usar la palabra clave
throws
en la firma del método para que el programa que llama sepa las excepciones que podrían ser lanzadas por el método. El método que llama podría manejar estas excepciones o propagarlas a su método llamador utilizando la palabra clavethrows
. Podemos proporcionar múltiples excepciones en la cláusulathrows
, y también se puede usar con el métodomain()
. - try-catch – Usamos el bloque
try-catch
para el manejo de excepciones en nuestro código.try
es el inicio del bloque ycatch
está al final del bloquetry
para manejar las excepciones. Podemos tener múltiples bloquescatch
con un bloquetry
. El bloquetry-catch
también puede estar anidado. El bloquecatch
requiere un parámetro que debe ser del tipoException
. - finalmente – el bloque
finally
es opcional y se puede utilizar solo con un bloquetry-catch
. Dado que una excepción detiene el proceso de ejecución, es posible que tengamos algunos recursos abiertos que no se cerrarán, por lo que podemos usar el bloquefinally
. El bloquefinally
siempre se ejecuta, ya sea que haya ocurrido una excepción o no.
Ejemplo de Manejo de Excepciones
- El método
testException()
está lanzando excepciones usando la palabra clavethrow
. La firma del método utiliza la palabra clavethrows
para informar al llamador sobre el tipo de excepciones que podría lanzar. - En el método
main()
, estoy manejando excepciones usando el bloquetry-catch
en el métodomain()
. Cuando no las estoy manejando, las propago a tiempo de ejecución con la cláusulathrows
en el métodomain()
. - El método
testException(-10)
nunca se ejecuta debido a la excepción y luego se ejecuta el bloquefinally
.
El método printStackTrace()
es uno de los métodos útiles en la clase Exception
con fines de depuración.
Este código producirá la siguiente salida:
Outputjava.io.FileNotFoundException: Negative Integer -5
at com.journaldev.exceptions.ExceptionHandling.testException(ExceptionHandling.java:24)
at com.journaldev.exceptions.ExceptionHandling.main(ExceptionHandling.java:10)
Releasing resources
Exception in thread "main" java.io.IOException: Only supported for index 0 to 10
at com.journaldev.exceptions.ExceptionHandling.testException(ExceptionHandling.java:27)
at com.journaldev.exceptions.ExceptionHandling.main(ExceptionHandling.java:19)
Algunos puntos importantes a tener en cuenta:
- No podemos tener una cláusula
catch
ofinally
sin una instruccióntry
. - A
try
statement should have eithercatch
block orfinally
block, it can have both blocks. - No podemos escribir ningún código entre bloques
try-catch-finally
. - Podemos tener varios bloques
catch
con una única instruccióntry
. - Los bloques
try-catch
pueden estar anidados de manera similar a las declaracionesif-else
. - Puede haber solo un bloque
finally
con una instruccióntry-catch
.
Jerarquía de Excepciones en Java
Como se mencionó anteriormente, cuando se genera una excepción, se crea un objeto de excepción. Las excepciones de Java son jerárquicas y se utiliza la herencia para categorizar diferentes tipos de excepciones. Throwable
es la clase principal de la jerarquía de excepciones de Java y tiene dos objetos secundarios: Error
y Exception
. Las Exception
s se dividen además en Exception
s Verificadas y Exception
s en Tiempo de Ejecución.
- Errores: Los
Error
s son escenarios excepcionales que están fuera del alcance de la aplicación y no es posible anticipar ni recuperarse de ellos. Por ejemplo, falla de hardware, bloqueo de la máquina virtual de Java (JVM) o error de falta de memoria. Es por eso que tenemos una jerarquía separada deError
s y no deberíamos intentar manejar estas situaciones. Algunos de losError
s comunes sonOutOfMemoryError
yStackOverflowError
. - Excepciones Verificadas: Las
Exception
s verificadas son escenarios excepcionales que podemos anticipar en un programa e intentar recuperarnos de ello. Por ejemplo,FileNotFoundException
. Deberíamos capturar esta excepción y proporcionar un mensaje útil al usuario y registrarla adecuadamente para propósitos de depuración. La claseException
es la clase principal de todas las excepciones verificadas. Si estamos lanzando una excepción verificada, debemoscatch
earla en el mismo método, o debemos propagarla al llamador usando la palabra clavethrows
. - Excepción en Tiempo de Ejecución: Las excepciones en tiempo de ejecución son causadas por una mala programación. Por ejemplo, intentar recuperar un elemento de un array. Deberíamos verificar primero la longitud del array antes de intentar recuperar el elemento, de lo contrario podría lanzar
ArrayIndexOutOfBoundException
en tiempo de ejecución.RuntimeException
es la clase principal de todas las excepciones en tiempo de ejecución. Si estamosthrow
eando alguna excepción en tiempo de ejecución en un método, no es necesario especificarlas en la cláusula de firma del métodothrows
. Las excepciones en tiempo de ejecución pueden evitarse con una mejor programación.
Algunos métodos útiles de las Clases de Excepción
Java \code{Exception} y todas sus subclases no proporcionan ningún método específico, y todos los métodos están definidos en la clase base – \code{Throwable}. Las clases \code{Exception} se crean para especificar diferentes tipos de escenarios de \code{Exception} para que podamos identificar fácilmente la causa raíz y manejar la \code{Exception} según su tipo. La clase \code{Throwable} implementa la interfaz \code{Serializable} para interoperabilidad.
Algunos de los métodos útiles de la clase \code{Throwable} son:
- public String getMessage() – Este método devuelve el mensaje \code{String} de \code{Throwable} y el mensaje puede proporcionarse mientras se crea la excepción a través de su constructor.
- public String getLocalizedMessage() – Este método se proporciona para que las subclases puedan anularlo para proporcionar un mensaje específico de la ubicación al programa que llama. La implementación de la clase \code{Throwable} de este método utiliza el método \code{getMessage()} para devolver el mensaje de excepción.
- public synchronized Throwable getCause() – Este método devuelve la causa de la excepción o \code{null} si la causa es desconocida.
- public String toString() – Este método devuelve la información sobre \code{Throwable} en formato \code{String}, el \code{String} devuelto contiene el nombre de la clase \code{Throwable} y el mensaje localizado.
- public void printStackTrace() – Este método imprime la información de la traza de la pila en el flujo de error estándar, este método está sobrecargado, y podemos pasar
PrintStream
oPrintWriter
como argumento para escribir la información de la traza de la pila en el archivo o flujo.
Mejoras en la Administración Automática de Recursos y en el Bloque Catch de Java 7
Si estás catch
eando muchas excepciones en un solo bloque try
, notarás que el código del bloque catch
consiste principalmente en código redundante para registrar el error. En Java 7, una de las características fue un bloque catch
mejorado donde podemos capturar múltiples excepciones en un solo bloque catch
. Aquí tienes un ejemplo del bloque catch
con esta característica:
Existen algunas limitaciones, como que el objeto de excepción es final y no podemos modificarlo dentro del bloque catch
, lee el análisis completo en Mejoras en el Bloque Catch de Java 7.
La mayor parte del tiempo, utilizamos el bloque finally
solo para cerrar los recursos. A veces olvidamos cerrarlos y obtenemos excepciones en tiempo de ejecución cuando los recursos se agotan. Estas excepciones son difíciles de depurar, y puede ser necesario revisar cada lugar donde estamos utilizando ese recurso para asegurarnos de cerrarlo. En Java 7, una de las mejoras fue try-with-resources
, donde podemos crear un recurso en la propia declaración try
y usarlo dentro del bloque try-catch
. Cuando la ejecución sale del bloque try-catch
, el entorno de ejecución cierra automáticamente estos recursos. Aquí tienes un ejemplo del bloque try-catch
con esta mejora:
A Custom Exception Class Example
Java proporciona muchas clases de excepciones para que las utilicemos, pero a veces es necesario crear nuestras propias clases de excepciones personalizadas. Por ejemplo, para notificar al llamador acerca de un tipo específico de excepción con el mensaje adecuado. Podemos tener campos personalizados para realizar un seguimiento, como códigos de error. Por ejemplo, supongamos que escribimos un método para procesar solo archivos de texto, de manera que podamos proporcionar al llamador el código de error apropiado cuando se envía otro tipo de archivo como entrada.
Primero, creamos MyException
:
Luego, creamos un CustomExceptionExample
:
Podemos tener un método separado para procesar diferentes tipos de códigos de error que obtenemos de diferentes métodos. Algunos de ellos se consumen porque es posible que no queramos notificar al usuario, o algunos de ellos los lanzaremos para notificar al usuario del problema.
Aquí estoy extendiendo Exception
para que cada vez que esta excepción se produzca, tenga que ser manejada en el método o devuelta al programa llamador. Si extendemos RuntimeException
, no es necesario especificarlo en la cláusula throws
.
Esta fue una decisión de diseño. El uso de Excepciones Verificadas tiene la ventaja de ayudar a los desarrolladores a comprender qué excepciones pueden esperar y tomar medidas apropiadas para manejarlas.
Mejores Prácticas para el Manejo de Excepciones en Java
- Usar Excepciones Específicas – Las clases base de la jerarquía de Exception no proporcionan información útil, por eso Java tiene tantas clases de excepción, como
IOException
con subclases adicionales comoFileNotFoundException
,EOFException
, etc. Siempre debemoslanzar
ycapturar
clases de excepción específicas para que el llamador pueda conocer fácilmente la causa raíz de la excepción y procesarlas. Esto hace que la depuración sea más fácil y ayuda a las aplicaciones cliente a manejar excepciones adecuadamente. - Lanzar Temprano o Fallar Rápido – Deberíamos intentar
lanzar
excepciones lo más temprano posible. Considera el métodoprocessFile()
anterior, si pasamos el argumentonull
a este método, obtendremos la siguiente excepción:
OutputException in thread "main" java.lang.NullPointerException
at java.io.FileInputStream.<init>(FileInputStream.java:134)
at java.io.FileInputStream.<init>(FileInputStream.java:97)
at com.journaldev.exceptions.CustomExceptionExample.processFile(CustomExceptionExample.java:42)
at com.journaldev.exceptions.CustomExceptionExample.main(CustomExceptionExample.java:12)
Mientras depuramos, tendremos que observar cuidadosamente el rastro de la pila para identificar la ubicación real de la excepción. Si cambiamos nuestra lógica de implementación para verificar estas excepciones temprano como se muestra a continuación:
Entonces, el rastro de la pila de la excepción indicará dónde ha ocurrido la excepción con un mensaje claro:
Outputcom.journaldev.exceptions.MyException: File name can't be null
at com.journaldev.exceptions.CustomExceptionExample.processFile(CustomExceptionExample.java:37)
at com.journaldev.exceptions.CustomExceptionExample.main(CustomExceptionExample.java:12)
- Capturar Tarde – Dado que Java obliga a manejar la excepción comprobada o declararla en la firma del método, a veces los desarrolladores tienden a
capturar
la excepción y registrar el error. Pero esta práctica es perjudicial porque el programa llamador no recibe ninguna notificación de la excepción. Deberíamoscapturar
excepciones solo cuando podamos manejarlas adecuadamente. Por ejemplo, en el método anterior, estoylanzando
excepciones de vuelta al método llamador para manejarlas. El mismo método podría ser utilizado por otras aplicaciones que podrían querer procesar la excepción de manera diferente. Mientras implementamos cualquier característica, siempre debemoslanzar
excepciones de vuelta al llamador y dejar que ellos decidan cómo manejarla. - Cierre de Recursos – Dado que las excepciones detienen el procesamiento del programa, deberíamos cerrar todos los recursos en el bloque finally o usar la mejora de Java 7
try-with-resources
para permitir que Java cierre los recursos por ti. - Registro de Excepciones – Siempre debemos registrar mensajes de excepción y al lanzar excepciones proporcionar un mensaje claro para que el llamante sepa fácilmente por qué ocurrió la excepción. Siempre debemos evitar un bloque
catch
vacío que simplemente consuma la excepción y no proporcione ningún detalle significativo de la excepción para la depuración. - Bloque único de captura para múltiples excepciones – La mayoría de las veces registramos los detalles de la excepción y proporcionamos un mensaje al usuario, en este caso, deberíamos usar la característica de Java 7 para manejar múltiples excepciones en un solo bloque
catch
. Este enfoque reducirá el tamaño de nuestro código y también se verá más limpio. - Uso de Excepciones Personalizadas – Siempre es mejor definir una estrategia de manejo de excepciones en el diseño y en lugar de lanzar y capturar múltiples excepciones, podemos crear una excepción personalizada con un código de error, y el programa llamante puede manejar estos códigos de error. También es una buena idea crear un método de utilidad para procesar diferentes códigos de error y usarlos.
- Convenciones de Nomenclatura y Empaquetado – Cuando crees tu excepción personalizada, asegúrate de que termine con
Exception
para que quede claro desde el nombre mismo que es una clase de excepción. Además, asegúrate de empaquetarlas como se hace en el Kit de Desarrollo de Java (JDK). Por ejemplo,IOException
es la excepción base para todas las operaciones de IO. - Usa las Excepciones con Moderación – Las excepciones son costosas, y a veces no es necesario lanzar excepciones en absoluto, podemos devolver una variable booleana al programa que llama para indicar si una operación fue exitosa o no. Esto es útil cuando la operación es opcional y no quieres que tu programa se bloquee porque falla. Por ejemplo, al actualizar las cotizaciones de acciones en la base de datos desde un servicio web de terceros, podemos querer evitar lanzar excepciones si la conexión falla.
- Documenta las Excepciones Lanzadas – Usa Javadoc
@throws
para especificar claramente las excepciones lanzadas por el método. Es muy útil cuando estás proporcionando una interfaz para que otras aplicaciones la utilicen.
Conclusión
En este artículo, aprendiste sobre el manejo de excepciones en Java. Aprendiste sobre throw
y throws
. También aprendiste sobre bloques try
(y try-with-resources
), catch
, y finally
.
Source:
https://www.digitalocean.com/community/tutorials/exception-handling-in-java