Java 41: Exception Hierarchy — Checked vs Unchecked & Custom Exceptions
easy⏱ 5 mincoursejava
El árbol de Throwable
Throwable tiene dos hijos: Error (a nivel JVM, irrecuperable — OutOfMemoryError, StackOverflowError; nunca los captures) y Exception. Bajo Exception, RuntimeException y su subárbol son unchecked; todo lo demás bajo Exception (IOException, SQLException) es checked — el compilador no compila a menos que hagas try/catch o añadas throws al método.
Throwable
├── Error (unchecked, don't catch)
└── Exception
├── RuntimeException (unchecked)
│ ├── NullPointerException
│ └── IllegalArgumentException
└── IOException (checked)
└── FileNotFoundException
Excepciones propias y encadenamiento de causa
Extiende Exception para un error de dominio checked, o RuntimeException para uno unchecked. Ofrece siempre un constructor (String message, Throwable cause) y llama a super(message, cause) para que el stack trace conserve el original. Relanzar con un tipo nuevo adjuntando la causa se llama traducción de excepciones — mantiene limpia la frontera de abstracción sin perder diagnóstico.
class OrderException extends Exception {
OrderException(String msg, Throwable cause) { super(msg, cause); }
}
try {
db.save(order);
} catch (SQLException e) {
throw new OrderException("could not persist order", e); // chain the cause
}
finally siempre se ejecuta
finally se ejecuta aunque try o catch ejecuten return, break o continue — e incluso si lanzan. Nunca hagas return desde finally: traga excepciones en silencio y sobrescribe el valor de retorno del try, ocultando bugs. Usa finally solo para limpieza; prefiere try-with-resources (próxima lección) para cerrar recursos.
Modela una excepción checked con causa
Construye las clases LowLevelError y DomainError (DomainError lleva una cause). Escribe loadUser(id) que lance LowLevelError si id<0, capturada y relanzada como DomainError con la causa adjunta. Escribe también withFinally() que 'retorne' un valor desde try mientras un bloque finally registra — demostrando que la limpieza corre. Imprime el mensaje de la causa encadenada.
Captura específico, no Exception
catch (Exception e) oculta errores de programación como NPE y es difícil de razonar. Captura el tipo más específico sobre el que puedas actuar; deja que el resto se propague. El multi-catch (catch (IOException | SQLException e)) maneja tipos no relacionados en un bloque cuando la recuperación es idéntica.
Quiz: diseño checked vs unchecked
¿Cuándo una nueva excepción debe ser checked? Cuando quien llama puede recuperarse razonablemente (reintentar, hacer fallback). Usa unchecked (RuntimeException) para errores de programación que quien llama no puede corregir en runtime (argumentos malos, estado ilegal). El estilo moderno se inclina a unchecked para evitar el ruido de throws — pero checked es correcto para fallos de I/O genuinamente recuperables.