Excepciones de Java
Una visión general de las excepciones en Java — qué son, la jerarquía de excepciones y por qué importa el manejo de excepciones.
Excepciones de Java
Una excepción es lo que ocurre cuando su programa se topa, en su camino actual, con una situación que no puede manejar — un archivo que no está, un número dividido entre cero, un índice de arreglo fuera de rango. En lugar de devolver una respuesta equivocada o corromper el estado en silencio, Java detiene el método actual, construye un objeto de excepción que describe qué salió mal y empieza a buscar código que sepa cómo lidiar con ello. Aprender la maquinaria de excepciones es aprender cómo Java le dice qué salió mal, dónde y qué podría hacer al respecto.
Qué es realmente una excepción
Una excepción es un objeto Java corriente. En concreto, una instancia de una clase que hereda de java.lang.Throwable. Cuando algo sale mal, la JVM (o su propio código) crea uno de estos objetos y lo lanza. Desde ese momento, el programa sigue un flujo de control distinto hasta que, o bien un bloque catch acepta la excepción, o bien el hilo termina.
El objeto transporta información: un tipo (la clase — NullPointerException, IOException, etc.), un mensaje (una descripción legible por humanos) y una traza de pila (la cadena de llamadas congelada en el momento en que se creó la excepción). Cuando lee una excepción en una consola, está leyendo esas tres cosas.
Exception in thread "main" java.lang.ArithmeticException: / by zero
at calc.Money.divide(Money.java:42)
at calc.App.main(App.java:11)La primera línea es tipo + mensaje. Las líneas indentadas son la traza de pila, la llamada más reciente primero.
Por qué excepciones en lugar de códigos de error
Los lenguajes más antiguos — C es el ejemplo clásico — señalan el fallo con códigos de retorno: cada función devuelve un int, usted lo comprueba, y si es negativo algo salió mal. Ese enfoque tiene dos problemas:
- Puede ignorar el valor de retorno. Un llamador que olvida comprobarlo no ve un fallo inmediato, y el error aflora más tarde en un lugar confuso.
- El camino de error ensucia el camino feliz. Cada línea de trabajo real va envuelta en
if (err) return err;.
Las excepciones invierten esto. Por defecto, una excepción no manejada detiene la ejecución, ruidosamente. Usted opta por manejarla allí donde realmente tiene una estrategia. El buen camino se mantiene limpio; el camino de recuperación está en su propio bloque.
Las tres cosas que pueden salir mal
Java ordena todo lo que es Throwable en tres cubos, y la diferencia importa porque el lenguaje los trata de forma distinta:
Error— la propia JVM está en apuros.OutOfMemoryError,StackOverflowError. Estos no se atrapan en código normal. Por lo general no hay nada útil que pueda hacer.RuntimeException(una subclase deException) — errores de programación que aparecen en tiempo de ejecución.NullPointerException,IndexOutOfBoundsException,ClassCastException. El compilador no le obliga a manejarlas, porque en código correcto no deberían ocurrir.Exceptioncomprobada (checked) (todo lo demás bajoException) — fallos recuperables que el programa debería anticipar.IOException,SQLException. El compilador exige que las atrape o las declare en la cláusulathrowsde su método.
La línea entre «error de programación» (excepción de runtime) y «fallo anticipado» (excepción comprobada) es una de las decisiones de diseño más discutidas de Java. Volveremos sobre ella en Excepciones comprobadas vs. no comprobadas en Java.
Cómo funcionan lanzar y atrapar
Cuando se ejecuta un throw, la JVM:
- Desenrolla la pila — el método actual termina abruptamente, luego su llamador hace lo mismo, y así sucesivamente.
- En cada marco, comprueba si hay un bloque
try { ... } catch (SomeType e) { ... }cuyocatchcoincida con el tipo de la excepción (o con un supertipo). - El primer
catchque coincide gana. El control salta allí, la pila deja de desenrollarse y la ejecución se reanuda en el bloque catch. - Si nada coincide, el hilo muere. En un programa de un solo hilo, eso significa que la JVM imprime la traza de pila y termina.
Por eso una excepción lanzada puede viajar a través de muchos métodos antes de ser atrapada — cada lanzamiento no atrapado burbujea un marco más arriba.
Una primera prueba
No tiene que escribir throw para encontrarse con una excepción — Java mismo las lanza cuando algo sale mal. Aquí el ejemplo más simple: dividir un entero entre cero. La JVM crea una ArithmeticException por usted.
Sin el try/catch, la tercera iteración haría caer todo el programa y la cuarta nunca se ejecutaría. Con él, el fallo queda contenido: obtenemos un mensaje de error y el bucle continúa. Esa capacidad — acotar el daño de un fallo — es todo el sentido de la maquinaria de excepciones.
Qué cubre esta parte del libro
Los capítulos restantes de esta parte son el vocabulario de trabajo del manejo de excepciones de Java, una pieza a la vez:
- La forma de
try/catch/finallyy para qué sirve cada cláusula. - Múltiples catch y el atajo multi-catch.
try-with-resourcespara todo lo que necesita cerrarse.- Lanzar excepciones usted mismo con
throw, y declararlas conthrows. - Comprobadas vs. no comprobadas: cuál usar y cuándo.
- La jerarquía de clases completa.
- Escribir sus propios tipos de excepción para su dominio.
- Los principios que distinguen un buen manejo de excepciones del ruido defensivo.
Léala en orden — cada capítulo da por supuesto el anterior.
Qué sigue
El manejo de excepciones empieza con la construcción más fundamental: el bloque try/catch. Continúe con Java try...catch.
Practice
A method `loadConfig()` reads a file and throws `IOException` on failure. The caller wraps the call in `try { loadConfig(); } catch (RuntimeException e) { ... }`. The IO fails. What happens?