Cuando programamos en Java, el bloque finally es una herramienta fundamental para asegurarnos de que cierto código se ejecute siempre, independientemente de si una operación dentro de un bloque try ha tenido éxito o ha lanzado una excepción. Este bloque se coloca al final de un bloque try-catch o incluso directamente después de un try sin necesidad de un catch. La sintaxis es sencilla: tras el último catch escribimos la palabra clave finally seguida de llaves {}, dentro de las cuales colocamos el código que queremos que se ejecute siempre.
Lo interesante del bloque finally es que su código se ejecuta tanto si el bloque try termina correctamente como si se lanza y captura una excepción. Incluso si dentro del try o catch hay un return, el código dentro de finally se ejecutará antes de que se devuelva el valor. Esto puede parecer un poco extraño, pero es una forma útil de garantizar que ciertas acciones ocurran siempre, como liberar recursos o hacer limpieza.
Por ejemplo, si hacemos una división sencilla dentro de un try y todo va bien, el código dentro de finally se ejecutará justo antes de que se imprima el resultado. Pero si la división provoca una excepción, como dividir entre cero, el bloque catch capturará esa excepción y podremos manejarla, y aun así el bloque finally se ejecutará después. Incluso si dentro del catch volvemos a lanzar la excepción con throw, el bloque finally se ejecutará antes de que la excepción se propague hacia afuera.
El uso más común y práctico del bloque finally es para liberar recursos que hemos abierto dentro del try. Por ejemplo, cuando trabajamos con archivos o conexiones de red, es crucial cerrar esos recursos para evitar fugas o bloqueos. Si no cerramos un archivo después de usarlo, podríamos dejarlo abierto y causar problemas en el sistema. Por eso, dentro del bloque finally solemos poner el código que cierra esos recursos, asegurándonos de que se ejecutará siempre, incluso si ocurre un error durante la lectura o escritura.
Un detalle importante es que las variables que queremos usar dentro del bloque finally deben estar declaradas fuera del bloque try, para que su alcance (scope) permita acceder a ellas. Por ejemplo, si declaramos un lector de archivos dentro del try, no podremos cerrarlo en el finally porque no estará visible allí. Por eso, declaramos la variable antes del try y luego la inicializamos dentro.
Aquí un ejemplo típico de cómo cerrar un archivo usando finally:
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("archivo.txt"));
String linea = reader.readLine();
System.out.println(linea);
} catch (IOException e) {
System.out.println("Error leyendo el archivo");
throw e;
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
System.out.println("Error cerrando el archivo");
}
}
}
En este código, aunque ocurra una excepción al leer el archivo, el bloque finally se asegura de que el lector se cierre correctamente. Incluso si lanzamos la excepción hacia afuera con throw, el cierre se ejecuta antes.
Sin embargo, esta forma tradicional de manejar recursos tiene un giro en Java moderno. Debido a que era tan común usar finally para cerrar recursos, el lenguaje introdujo una sintaxis más limpia llamada try-with-resources. Esta construcción permite declarar los recursos dentro del paréntesis del try, y Java se encarga automáticamente de cerrarlos al finalizar el bloque, sin necesidad de escribir un bloque finally explícito.
Por ejemplo, usando try-with-resources para leer un archivo, el código sería así:
try (BufferedReader reader = new BufferedReader(new FileReader("archivo.txt"))) {
String linea = reader.readLine();
System.out.println(linea);
} catch (IOException e) {
System.out.println("Error leyendo el archivo");
}
Aquí, no necesitamos preocuparnos por cerrar el lector, porque Java lo hace automáticamente al salir del bloque try. La única condición es que los recursos que declaremos deben implementar la interfaz Closeable, es decir, deben tener un método close() que Java pueda invocar.
En definitiva, el bloque finally sigue siendo útil para ejecutar código que debe correr siempre, pero para el manejo de recursos, try-with-resources es la forma recomendada y más limpia en Java actual. Aun así, conocer cómo funciona finally nos ayuda a entender mejor el control de flujo y la gestión de recursos en Java.