Java try...catch
Maneja errores en tiempo de ejecución en Java con bloques try...catch para mantener tu programa en funcionamiento cuando algo falla.
try/catch es la unidad más pequeña de manejo de excepciones en Java. Envuelves el código arriesgado en try, y lo sigues con uno o más bloques catch que indican qué hacer si se lanza un tipo particular de excepción. Juntos forman una sola sentencia; no puedes tener uno sin el otro (o sin un finally, que veremos en un capítulo posterior).
Anatomía
try {
// code that might throw
} catch (ExceptionType e) {
// code that runs only if a matching exception was thrown
}Hay algunas cosas que son fáciles de pasar por alto a primera vista:
- La variable en el
catch(epor convención) tiene alcance al bloque catch. Puedes nombrarla como quieras;eyexson las opciones más comunes. - El parámetro del catch es efectivamente final — puedes leerlo pero no deberías reasignarlo. Tratarlo como inmutable mantiene el código fácil de seguir.
- El bloque
trytiene su propio alcance. Las variables declaradas dentro de él no son visibles en elcatchni después de la sentencia. Si necesitas un valor calculado en eltrymás adelante, decláralo fuera.
String content;
try {
content = Files.readString(path);
} catch (IOException e) {
content = ""; // works because `content` was declared outside the try
}
System.out.println(content);Qué capturas coinciden
Un bloque catch (T e) se ejecuta cuando la excepción lanzada es una instancia de T — incluyendo cualquier subtipo de T. Por lo tanto:
catch (Exception e)captura casi todo:IOException,NullPointerException, tus propias excepciones personalizadas.catch (RuntimeException e)captura errores en tiempo de ejecución pero no excepciones verificadas comoIOException.catch (NullPointerException e)captura solo NPEs (y subclases, de las cuales normalmente no hay ninguna).
Dentro del try, solo se ejecuta el primer catch coincidente. Si listas un tipo más amplio antes que uno más estrecho, el catch más estrecho es inalcanzable y el compilador lo rechaza:
try { ... }
catch (Exception e) { ... } // matches everything
catch (IOException e) { ... } // ERROR: unreachableSiempre lista los catches de más específico a más general.
Qué hacer en un catch
Un bloque catch no es un lugar para hacer desaparecer las excepciones. Es un lugar para decidir qué hacer ante un fallo conocido. Las opciones realistas son:
- Recuperarse — reintentar, recurrir a un valor por defecto, cambiar a un recurso diferente. Este es el mejor caso, y es más raro de lo que piensas.
- Registrar y relanzar — guardar los detalles y dejar que la excepción siga propagándose a un manejador de nivel superior que sepa qué hacer.
- Envolver y relanzar — traducir una excepción de bajo nivel a una que encaje en el vocabulario de esta capa (
IOException→ConfigLoadException). - Traducir a un valor de retorno — cuando el fallo es genuinamente esperado (por ejemplo, al parsear entrada del usuario), retornar
Optional.empty()o un valor centinela.
Lo que no hay que hacer es capturar una excepción y continuar silenciosamente sin registrarla ni actuar sobre ella. Así es como los errores desaparecen en el aire. Volveremos a esto en el capítulo de buenas prácticas.
Leer la información de una excepción
El parámetro del catch es un objeto. Tres métodos que usarás constantemente:
e.getMessage()— la descripción legible por humanos. Puede sernull.e.toString()— nombre de la clase más el mensaje, por ejemplojava.io.IOException: file not found.e.printStackTrace()— escribe el rastro enSystem.err. Conveniente durante la depuración, pero usa un logger real en código de producción (enruta el rastro por el mismo canal que todo lo demás).
Un cuarto método, e.getCause(), devuelve la excepción subyacente cuando una fue envuelta — útil cuando necesitas entender el fallo original dentro de una capa de traducción.
Capturar Exception vs. capturar Throwable
Puedes escribir catch (Throwable t) y capturará todo — incluyendo Errors como OutOfMemoryError. No lo hagas. Los Errors significan que la JVM está en problemas y tu código no está en condiciones de reaccionar de manera sensata a ellos. Capturar Throwable enmascara errores que deberían colapsar el proceso.
Limítate a Exception o uno de sus subtipos. Si genuinamente necesitas registrar lo inesperado antes de dejarlo morir, la forma correcta es catch (RuntimeException e) { log; throw; }.
Un ejemplo trabajado
Una pequeña utilidad que parsea un entero de cada línea de entrada. Algunas líneas son números válidos, otras no, y una es null. Usamos try/catch para continuar más allá de las líneas incorrectas y contar lo que ocurrió. Llamar a line.trim() lanza una NullPointerException en la entrada null, mientras que Integer.parseInt lanza una NumberFormatException en cualquier cosa que no sea un entero — dos tipos de excepción distintos, cada uno con su propio catch.
La salida es:
not an integer: "hello"
null line — skipped
not an integer: "3.14"
---
parsed: 3, failed: 3, sum: 118
Dos cosas a observar. Primero, el programa termina. Sin los catches, la primera línea incorrecta terminaría la ejecución y nada después de ella se contaría. Segundo, los dos fallos se distinguen: la línea null toma la rama NullPointerException e imprime su propio mensaje, mientras que "hello" y "3.14" toman la rama NumberFormatException. Seleccionar catches por tipo es cómo evitas que diferentes fallos se mezclen.
Qué sigue
Un solo catch es el caso más sencillo. El código real generalmente tiene que manejar varios fallos diferentes a la vez. Continúa con Bloques múltiples catch en Java y multi-catch. Después de eso, el bloque finally y try-with-resources completan la sintaxis, mientras que throw y throws cubren el otro lado: señalar fallos desde tu propio código.