1. El Árbol Genealógico de Throwable
Toda anomalía en Java desciende de Throwable. La rama Error representa fallos catastróficos de la JVM (no se atrapan). La rama Exception se divide entre Checked (obligatorias por compilador) y RuntimeException (bugs lógicos unchecked).
// Checked: Obligación sintáctica
public void leerArchivo() throws IOException {
new FileInputStream("datos.csv");
} 2. Anatomía de una Excepción en Memoria
Una excepción no es una simple señal de error: es un OBJETO REAL en el Heap. Al crearse con new, captura una instantánea exacta de la pila de llamadas (StackTrace), un mensaje explicativo y opcionalmente una causa original.
if (saldo < monto) {
throw new SaldoInsuficienteException(
"Saldo insuficiente: requerido " + monto
);
} 3. Desenrollado de la Pila (Stack Unwinding)
Cuando ocurre una excepción y el método no tiene catch, su marco de ejecución colapsa de inmediato y el error se propaga hacia el método que lo llamó, subiendo hasta encontrar un catch o tirar la JVM.
try {
servicio.procesarOrden();
} catch (SQLException e) {
// main() captura el error y rescata el hilo
LOGGER.error("Fallo en BD: " + e.getMessage());
} 4. Máquina de Estados: try-catch-finally
El bloque finally se ejecuta SIEMPRE: tanto si el try termina con éxito, como si ocurre una excepción atrapada, o incluso si la excepción no coincide y sigue subiendo por la pila.
try {
// operacion()
} catch (Exception e) {
// manejo()
} finally {
conexion.cerrar(); // Siempre garantizado
} 5. try-with-resources y AutoCloseable
Desde Java 7, cualquier recurso que implemente AutoCloseable se puede abrir en los paréntesis del try. La JVM garantiza llamar a close() automáticamente al salir del bloque, incluso ante excepciones.
// Varios recursos separados por punto y coma
try (var in = new FileInputStream(f1);
var out = new FileOutputStream(f2)) {
in.transferTo(out);
} 6. Excepciones Personalizadas y Causas Encadenadas
Traducí errores de infraestructura de bajo nivel a conceptos del dominio usando el constructor con cause: throw new PagoFallidoException("No se pudo cobrar", rootCause). Así se conserva el stack trace original sin exponer detalles crudos al usuario.
public class PagoException extends Exception {
public PagoException(String msg, Throwable cause) {
super(msg, cause); // Encadenamiento
}
} 7. Los 4 Antipatrones a Evitar
Errores que destruyen la observabilidad y causan fallos silenciosos en producción. Identificalos y evitalos en tus proyectos.
// O lo manejás de verdad, o lo dejás subir
try {
debitar();
} catch (SaldoInsuficienteException e) {
notificarCliente(e.getMessage()); // Acción concreta
}