El manejo de excepciones es un punto muy importante de cualquier programa, en esta página se explica todo lo concerniente a Manejo de Excepciones en Java, sintaxis y principales usos.
Si quieres saber más sobre la historia y funcionamiento de Java visita este link.
1. Generalidades del Manejo de Excepciones en Java
En general un programa puede fallar por diversas razones, por ejemplo un error de código, un recurso no disponible o un valor de una entrada incorrecto. Y aunque existen muchas otras razones para que se produzcan fallos, es muy importante al momento de programar tener en cuenta estos posibles errores y tratarlos.
Una excepción es la forma de decir que algo no funcionó como se esperaba, aquí es bueno considerar que la forma tradicional de manejar estos casos ha sido con códigos de error, sin embargo un programa profesional debería tener un manejo robusto de errores, lo cual implica en Java, la definición de diferentes clases que puedan representar errores y el manejo apropiado de los mismos.
2. Árbol de Excepciones, Excepciones verificadas y no verificadas
En Java no tenemos que empezar desde cero a escribir las excepciones, ya existen una gran cantidad de clases que las pueden representar. En la figura 1, se muestran la jerarquía de estas excepciones.
Esta imagen muestra las principales clases para manejar excepciones todas ellas pertenecientes al paquete java.lang.
Las excepciones Error y RuntimeException y las que derivan de ellas vía herencia, se llaman excepciones no verificadas, con lo cual cuando un método lanza estas excepciones no es necesario realizar el proceso de captura como se explicará más adelante. En el caso de las excepciones no verificadas estas aparecerán en tiempo de ejecución causando la terminación del programa de forma abrupta sino fueron debidamente capturadas.
Las Excepciones referidas como Excpetion y derivadas de esta clase deben ser capturadas de forma obligatoria o el compilador arrojará un mensaje de error.
Como nota adicional, las excepciones siempre terminan en la palabra Exception, esto es más una convención que algo obligatorio, pero es bueno seguirla pues es la costumbre entre los desarrolladores de Java.
3. Lanzando y capturando Excepciones
Para lanzar una excepción basta que un método propio del entorno de Java o de una librería lo lance o también se puede realizar un lanzamiento manual a través de la palabra reservada throw, que requerirá una instancia de la clase de Excepción que se va a lanzar.
throw new Exception("Error detail");
Para capturar una Excepción basta con ejecutar el código que puede lanzar una excepción usando la estructura de control try – catch, que en general se muestra en la siguiente fragmento de código.
try {
// Código susceptible de error
} catch(TipoExcepcion_1 ex1) {
// Procesamiento del error
} catch(TipoExcepcion_2 ex2) {
// Procesamiento del error
} catch(TipoExcepcion_n exn) {
// Procesamiento del error
} finally {
// Procesamiento obligatorio
}
En la sección try, se empezará a ejecutar el código, tan pronto se lance una excepción se dejará de ejecutar código y se continuará con la verificación de las cláusulas catch.
Los bloques catch atraparan la primera excepción que coincida con el error lanzado en la sección try. El orden de los catch es importante porque solo se procesará el primero. Además es conveniente indicar que la sección finally siempre se ejecutará, esta normalmente es reservada para liberar recursos.
Es frecuente encontrar que en los bloques catch o finally se vuelvan a lanzar excepciones en caso que el bloque o el punto donde se encuentra el código no sea capaz de manejarlo, a esto se le conoce como lanzamiento de segundas excepciones.
4. Manejo de Excepciones en los métodos
Cuando creamos un método en Java es posible que el mismo no tenga la capacidad o la suficiente información como para manejar alguna excepción en este caso es posible indicar en la firma del método, las excepciones que no se manejan allí y que puede eventualmente lanzar. En estos casos, la parte del código que llama a estos métodos deberá capturarlos con estructuras try – catch o a su vez volverlos a indicar que no puede manejar las excepciones. Estos e puede repetir, método tras método hasta que alguno de ellos maneje la excepción o hasta que se llegue al punto de entrada del programa, es decir al main.
ReturnType name(parameters) throws ClassException1, ClassException2 {
}
Note lo siguiente:
- Al método main se le puede agregar la clausula throws
- No es lo mismo throw para lanzar excepciones, que throws para indicar que un método puede lanzar esas Excepciones.
- La cláusula throws puede tener varias clases de Excepción separadas por coma.
- Las cláusulas throws pueden servir para diferenciar métodos con los mismos nombres, por lo cual, estas excepciones sirven para sobrecargar los métodos.
5. Excepciones personalizadas
El programador puede crear sus propias clases de Excepción para representar situaciones que deban ser controladas según el dominio del software en cuestión. Para hacer esto basta con heredar de alguno de las clases típicas de Excepción, mostrada en la Figura 1. Normalmente será heredado de Exception.
Adicionalmente, es común que se provea un mensaje explicativo de la Excepción redefiniendo el contenido del atributo message en la clase Exception.
6. Try – Catch con recursos
En programación es muy habitual que se manejen recursos, como archivos, páginas web y conexiones a bases de datos, todos estos siguen una regla general y es que cualquier recurso que se abra debe cerrarse, en caso de no hacerlo se corren grandes riegos de problemas debido agotar recursos, concurrencia y en general efectos indeseados difíciles de detectar.
Debido a lo habitual que es que los programadores olviden cerrar los recursos existe una variante de la estructura try – catch llamada try – catch with resources, que cierra automáticamente estos recursos por el programador.
La forma de definirlo es muy similar simplemente se debe declarar aquellos recursos que se puedan cerrar en los paréntesis del try tal como se muestra en el código 4.
try (FileReader fr = new FileReader(path);
BufferedReader br = new BufferedReader(fr)) {
return br.readLine();
}
Para que los recursos puedan ser utilizados entre los paréntesis deben haber implementado la interface AutoCloseable o Closeable y al final de del try se ejecutaran los métodos close de los respectivos recursos.