W3docs

Excepciones Checked vs. Unchecked en Java

Diferencia entre excepciones checked y unchecked en Java y cuándo usar cada una.

Java divide sus excepciones en dos grupos, y el compilador las trata de forma distinta. Las excepciones checked deben capturarse o declararse; el compilador rechaza construir código que las ignore. Las excepciones unchecked no tienen ese requisito — el compilador permite que se propaguen silenciosamente y solo el entorno de ejecución las captura. Esta división es una de las decisiones más distintivas y debatidas de Java, y entenderla define cómo se escribe código robusto.

Cuál es cuál

Todo lo que es Throwable cae en uno de tres grupos:

  • Error — catástrofe a nivel de la JVM. Unchecked. No capturar.
  • RuntimeException (subclase de Exception) y todos sus subtipos. Unchecked.
  • Otras subclases de Exception. Checked.

La regla es: "RuntimeException y Error más sus subtipos son unchecked; todo lo demás bajo Throwable es checked." Es una distinción en el árbol de clases, no un indicador de configuración — no hay forma de hacer que una RuntimeException sea checked o que una IOException sea unchecked.

Qué significa "checked" en el código

Si el cuerpo de un método lanza una excepción checked, el método debe hacer una de dos cosas, o el programa no compilará:

// Option 1: catch it
public void load() {
  try {
    String text = Files.readString(path);   // throws IOException
  } catch (IOException e) {
    // ...
  }
}

// Option 2: declare it
public void load() throws IOException {
  String text = Files.readString(path);
}

El compilador recorre cada método y verifica: para cada excepción checked que pueda escapar de él, ¿hay un catch contenedor o una cláusula throws? Si no, error.

Las excepciones unchecked no tienen ese requisito. Puedes lanzar una IllegalArgumentException desde cualquier método sin modificar la firma, y cualquier llamador es libre de capturarla o no.

La idea de diseño

La motivación detrás de las excepciones checked fue: algunos fallos son resultados esperados de la operación — un archivo podría no existir, una red podría estar caída — y el lenguaje debería asegurarse de que los llamadores los consideren. Las excepciones unchecked son para errores de programación — desreferencias de null, índice fuera de rango, argumentos ilegales — donde la solución correcta es corregir el código, no agregar un try/catch.

Esa distinción parece clara. En la práctica se dobla:

  • El código en capas multiplica las declaraciones. Una IOException de bajo nivel proveniente de un cargador de configuración no se queda allí — cada método que la llama debe manejarla o declararla. Para cuando la excepción llega al controlador, la firma enumera seis excepciones checked no relacionadas.
  • Las lambdas no las manejan bien. Stream.map(this::parseLine) no compilará si parseLine lanza una excepción checked, porque el método apply de Function no la declara.
  • El envoltura está en todos lados. Una solución alternativa común es capturar una excepción checked inmediatamente y relanzarla como una excepción de tiempo de ejecución, lo que anula el propósito original.

La mayor parte del código Java moderno usa menos excepciones checked que la biblioteca estándar — a veces ninguna en absoluto. Los frameworks nuevos como Spring se apoyan casi por completo en excepciones de tiempo de ejecución por esa razón.

Cuándo usar cada una

Una regla práctica defendible:

  • Usa checked cuando el llamador tenga una estrategia de recuperación realista específica para el fallo — por ejemplo, reintentar ante un error de red, recurrir a una alternativa ante un fallo de análisis. Obliga al llamador a pensar en ello.
  • Usa unchecked cuando el fallo sea un bug (IllegalArgumentException, NullPointerException, IllegalStateException) o cuando ningún llamador pueda recuperarse razonablemente.

En caso de duda, prefiere unchecked. El costo de equivocarse es menor, y siempre puedes ajustar después.

La experiencia desde el catch

Que una excepción sea checked o no cambia el aspecto del catch:

// checked: caller must catch or declare
try {
  parser.parse(path);
} catch (IOException e) {
  // handle
}

// unchecked: caller may catch but doesn't have to
try {
  return numbers.get(idx);
} catch (IndexOutOfBoundsException e) {
  return -1;
}

Desde la perspectiva del catch no hay diferencia de comportamiento — ambos bloques capturan lo que declaran. La división solo importa en el sitio de llamada que no capturó: una checked forzaría una declaración throws; una unchecked se propaga silenciosamente.

Un error común: capturar Exception

Debido a que Exception es el padre de los tipos checked y unchecked (excepto Error), catch (Exception e) coincide con casi todo. Esto es a menudo un olor a código:

try { complexOperation(); }
catch (Exception e) { log("failed"); }    // hides bugs and real failures alike

El problema no es que capture — es que captura demasiado. Una NullPointerException aquí indica un bug; una IOException indica un fallo real recuperable; una RuntimeException de una biblioteca que no controlas puede ser cualquier cosa. Tratarlas de forma idéntica suele significar no manejar ninguna de ellas bien. Prefiere capturar los tipos específicos para los que tienes un plan.

Un ejemplo completo

Un pequeño programa con dos métodos: uno declara una IOException checked y el otro lanza una IllegalArgumentException unchecked. El compilador los trata de forma diferente — solo la checked fuerza el manejo en el sitio de llamada.

java— editable, runs on the server

Observa tres cosas. Elimina el throws java.io.IOException de maybeReadFile y el archivo no compilará. Elimina el try/catch circundante de la primera llamada y el archivo tampoco compilará. Pero no hay try/catch alrededor del último requirePositive(-7) — y eso compila bien, aunque la llamada vaya a colapsar el programa. Eso es el tratamiento asimétrico en una sola pantalla.

Qué sigue

Hemos hablado de tipos como IOException, RuntimeException, Error — pero ¿cómo se relacionan realmente? El árbol de clases es pequeño y vale la pena memorizarlo. Continúa en jerarquía de excepciones en Java.

Práctica

Práctica
¿Cuál afirmación sobre las excepciones checked es correcta?
¿Cuál afirmación sobre las excepciones checked es correcta?
Was this page helpful?