Try-Catch-Finally y Jerarquía de Excepciones
En las aplicaciones, a veces pueden ocurrir problemas que esperamos y otras veces que no. En Java, para gestionar este tipo de errores utilizamos los bloques llamados try-catch.
En esta aplicación, comenzaremos desde lo más básico hasta llegar a un nivel avanzado.
Hablemos sobre los errores. Por ejemplo, tengamos un String.
String sehir = "Isparta";
int sayi = sehir;
El código anterior nunca funcionará. Esto es un error a nivel del lenguaje. Este código no se puede compilar. Es decir, no se puede convertir a bytecode.
Pero los errores que gestionaremos al usar try-catch no serán como los del ejemplo anterior. Nuestros escenarios serán errores que podremos capturar cuando la aplicación se haya compilado y convertido a bytecode. Para eso utilizamos try-catch.
Escribamos un Array de Integers.
Integer[] array = new Integer[]{1,2,3};
System.out.println(array[0]);
En la salida del Array anterior, estamos imprimiendo el índice 0 del array que creamos. La salida que obtendremos será 1.
Integer[] array = new Integer[]{1,2,3};
System.out.println(array[5]);
Si modificamos este código e intentamos imprimir el índice 5, el compilador no nos dará un error. Pero sabemos que esto es un error. Porque ese array no tiene índice 5. Por lo tanto, cuando ejecutemos la aplicación, Java lanzará una Excepción. Junto con ello, también recibiremos un mensaje como "ArrayIndexOutOfBoundsException".
En este punto, necesitamos que nuestro código gestione los errores.
try {
Integer[] array = new Integer[]{1, 2, 3};
System.out.println(array[5]);
} catch (Exception e) {
System.out.println("Ocurrió un Error");
}
Los bloques try-catch anteriores son un estándar. Un bloque try o catch por sí solo no tendrá sentido. El interior del bloque catch generalmente se escribe como "Exception". Esto significa que el bloque try intenta ejecutarse. Si se lanza un error desde alguno de los códigos, se cae al bloque catch. El error que recibimos antes, llamado "ArrayIndexOutOfBoundsException", se pasa como parámetro a la clase "Exception" en el bloque catch. Es decir, la aplicación no se rompe, por así decirlo. En este punto, en los fragmentos de código anteriores, se ejecutará al menos una vez, ya sea try o catch.
Así como mostramos el mensaje "Ocurrió un Error" en esta aplicación, también podemos imprimir el error mismo. Si revisamos el bloque catch del código que escribimos arriba como se muestra a continuación, habremos alcanzado el resultado deseado.
} catch (Exception e) {
System.out.println(e);
}
Este fragmento de código será la prueba de que el error lanzado se pasa como parámetro al bloque catch.
Cuando se ejecute la aplicación, el error que obtendremos será nuevamente en forma de "ArrayIndexOutOfBoundsException".
Finally También tenemos un bloque finally. Su significado en turco es "finalmente". Este bloque se ejecuta siempre, ya sea que se ejecute try o catch.
Actualicemos el código anterior.
try {
Integer[] array = new Integer[]{1, 2, 3};
System.out.println(array[5]);
} catch (Exception e) {
System.out.println("Ocurrió un Error");
} finally {
System.out.println("¡El Bloque Finally Siempre se Ejecuta!");
}
Como sabemos en nuestro escenario, intentamos acceder al índice 5. En este caso, se nos lanza una Excepción y caemos al bloque catch. Luego se ejecuta el bloque finally y ha mostrado el mensaje "¡El Bloque Finally Siempre se Ejecuta!".
try {
Integer[] array = new Integer[]{1, 2, 3};
System.out.println(array[2]);
} catch (Exception e) {
System.out.println("Ocurrió un Error");
} finally {
System.out.println("¡El Bloque Finally Siempre se Ejecuta!");
}
Esta vez intentamos acceder al índice 2 en el bloque try. Esta vez el bloque catch no se ejecuta y, a pesar de ello, el bloque finally se ejecutará nuevamente mostrando el mensaje "¡El Bloque Finally Siempre se Ejecuta!".
Este bloque se utiliza con mucha frecuencia en escenarios reales para la gestión de conexiones como streams, bases de datos, etc.
Tomemos como ejemplo conceptual una conexión a base de datos. Necesitamos conectarnos a la base de datos y cerrar esta conexión al finalizar las operaciones. Esta situación debe aplicarse tanto si ocurre un error como si no.
Este enfoque existe en todos los lenguajes de programación orientada a objetos.
Jerarquía de Excepciones Para entender mejor la estructura Try-Catch-Finally, también es necesario dominar la jerarquía. En los lenguajes de programación orientada a objetos, la jerarquía generalmente no cambia. Por supuesto, las clasificaciones pueden variar de un lenguaje a otro, pero en este artículo hablaremos sobre Java.
Examinemos la siguiente imagen.
[imagen]
Las clases Exception y Error heredan de la clase llamada Throwable. Las excepciones son errores relacionados con el código que escribimos, independientemente de la plataforma en la que se ejecute. Sin embargo, la clase Error se refiere a errores que no están bajo el control del programador y que están totalmente relacionados con la gestión del sistema operativo. Las excepciones, por otro lado, se pueden controlar con código. Estas se gestionan completamente con las estructuras try-catch.
Nuestra área de interés es la jerarquía de Excepciones en la imagen.
Cuando examinamos la imagen, vemos una nueva distinción. Vemos las clases RuntimeException, IOException, SQLException y AWTException debajo de la clase Exception.
- "IOException": Errores que obtendremos durante operaciones de Stream Input/Output (Operaciones con Archivos).
- "SQLException": Errores que se obtendrán durante la conexión a la base de datos o consultas. etc.
Si observamos, todas estas clases son hijas de la clase Exception. Si prestamos atención a RuntimeException, podemos ver el error "ArrayIndexOutOfBoundsException" que obtuvimos en nuestro ejemplo anterior.
Lo que debemos leer y ver aquí es que los errores que obtenemos se heredan de manera jerárquica. Es decir, existe una relación de herencia.
- "ArithmeticException": Errores numéricos, puede lanzarse cuando se excede el valor numérico determinado.
- "NullPointerException": Es uno de los errores más comunes. Se lanza cuando se intentan usar clases que no tienen referencia.
- "ClassCastException": Son errores de conversión de tipo. Son errores que no se pueden convertir entre sí o que no se pueden asignar.
- "IndexOutOfBoundsException": Son errores relacionados con arrays. Se explicó en nuestro ejemplo anterior.
Estas 4 Excepciones que mencionamos y otras 2 clases de Excepciones que no mencionamos, se extienden (extends) de "RuntimeException".
Estas 6 clases de Excepción, a diferencia de otras clases, se denominan Unchecked Exception. Java no verifica (check) estas Excepciones y delega esta tarea al programador. Por lo tanto, debemos controlar estas Excepciones con try-catch. Pero las otras, por ejemplo: "IOException, SQLException, AWTException", a diferencia de las unchecked exception, se denominan Checked Exception. Estas clases tienen una situación especial. Java nos advierte en los puntos donde pueden ocurrir las Excepciones anteriores y solicita que sean "Manejadas" (Handle).