W3docs

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 (e por convención) tiene alcance al bloque catch. Puedes nombrarla como quieras; e y ex son 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 try tiene su propio alcance. Las variables declaradas dentro de él no son visibles en el catch ni después de la sentencia. Si necesitas un valor calculado en el try má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 como IOException.
  • 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: unreachable

Siempre 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 (IOExceptionConfigLoadException).
  • 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 ser null.
  • e.toString() — nombre de la clase más el mensaje, por ejemplo java.io.IOException: file not found.
  • e.printStackTrace() — escribe el rastro en System.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.

java— editable, runs on the server

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.

Práctica

Práctica
Un bloque try ejecuta `a[5] = 1;` en un array de 2 elementos, con `catch (RuntimeException e)` escrito primero y `catch (ArrayIndexOutOfBoundsException e)` escrito segundo. ¿Qué ocurre?
Un bloque try ejecuta `a[5] = 1;` en un array de 2 elementos, con `catch (RuntimeException e)` escrito primero y `catch (ArrayIndexOutOfBoundsException e)` escrito segundo. ¿Qué ocurre?
Was this page helpful?