Bloque finally en Java: mitos, realidad y trampas
\n\nCada desarrollador Java, desde principiante hasta senior, tarde o temprano se encuentra con el bloque finally. En los manuales escriben: "Este bloque se ejecuta siempre, independientemente de si ocurrió una excepción o no". Suena como una garantía de hierro, ¿verdad? Pero echemos un vistazo detrás de escena.
En StackOverflow, la pregunta "Does a finally block always get executed in Java?" ha acumulado miles de visitas y cientos de comentarios. Programadores experimentados discuten, los novatos se pierden, y los casos reales muestran: "siempre" no es una verdad absoluta. En este artículo analizaremos todos los escenarios, escribiremos código real y descubriremos cuándo finally puede fallarte.
¿Cómo funciona finally? Ejemplo básico
\n\nPara empezar, lo clásico. El bloque finally se utiliza para liberar recursos: cerrar archivos, conexiones a bases de datos, sockets. Se ejecuta después de try (y catch, si hubo excepción), pero antes de que el control abandone el método.
public class FinallyDemo {\n public static void main(String[] args) {\n try {\n System.out.println("Dentro de try");\n int result = 10 / 2; // Todo bien\n } catch (ArithmeticException e) {\n System.out.println("Excepción capturada: " + e.getMessage());\n } finally {\n System.out.println("¡Bloque finally ejecutado!");\n }\n System.out.println("El programa continúa...");\n }\n}\n\nSalida:
\nDentro de try\n¡Bloque finally ejecutado!\nEl programa continúa...\n\nTodo predecible. Pero, ¿qué sucede si en try ocurre una excepción que no es capturada?
public class FinallyWithUncaught {\n public static void main(String[] args) {\n try {\n System.out.println("Intentando dividir por cero...");\n int result = 10 / 0; // ¡Excepción!\n } finally {\n System.out.println("Finally se ejecuta ANTES de que la excepción suba");\n }\n // Esta línea ya no se ejecutará\n System.out.println("Esta línea no se imprimirá");\n }\n}\n\nLa salida sorprenderá a los novatos:
\nIntentando dividir por cero...\nFinally se ejecuta ANTES de que la excepción suba\nException in thread "main" java.lang.ArithmeticException: / by zero\n at FinallyWithUncaught.main(FinallyWithUncaught.java:5)\n\nEl bloque finally se ejecutó, aunque la excepción no fue manejada. Este comportamiento es el que generó el mito del "siempre". Pero profundicemos.
¿Cuándo NO se ejecuta finally? Escenarios reales
\n\nExisten al menos tres situaciones en las que finally puede no ejecutarse. Recuérdalas: te ahorrarán horas de depuración.
1. System.exit() — terminación de emergencia de la JVM
\n\nEl caso más conocido. Si dentro del bloque try se llama a System.exit(0), la JVM finaliza instantáneamente y finally no se ejecuta.
public class FinallyAndExit {\n public static void main(String[] args) {\n try {\n System.out.println("Iniciando trabajo...");\n System.exit(0); // La JVM termina aquí\n } finally {\n // ¡Este código NO se ejecutará!\n System.out.println("¿Pensaste que me ejecutaría? ¡Pues no!");\n }\n }\n}\n\nSalida:
\nIniciando trabajo...\n\n¿Por qué sucede esto? Porque System.exit() detiene la máquina virtual a nivel del sistema operativo. No es una excepción, es una orden de "morir". El bloque finally simplemente no tiene tiempo de ejecutarse.
System.exit() en tu código, deja de hacerlo. Es de mal gusto. Para finalizar un programa, usa