Lanzar excepciones

throw, throws y diseño de contratos

throw: lanzar una excepción

throw lanza una instancia de Throwable. Detiene la ejecución normal y desencadena la búsqueda de un catch compatible hacia arriba en la pila de llamadas. Si no se encuentra ninguno, la JVM termina el hilo y muestra el stack trace.

// throw: lanzar una excepción
public int dividir(int a, int b) {
    if (b == 0) throw new ArithmeticException("Divisor no puede ser cero");
    return a / b;
}

// Siempre se lanza una instancia — nunca la clase
// throw new IllegalArgumentException("mensaje");   // correcto
// throw IllegalArgumentException;                  // error de compilación

// Relanzar la misma excepción
try {
    operacionRiesgosa();
} catch (IOException e) {
    log.error("Fallo E/S", e);
    throw e; // relanza la misma instancia (preserva el stack trace original)
}

throws: declarar en la firma

throws declara que un método puede lanzar una o más checked exceptions. Es parte del contrato del método y el compilador obliga al llamador a manejarlas. Para unchecked exceptions (RuntimeException) no es obligatorio declararlas, aunque algunos equipos lo hacen por documentación.

// throws: declarar en la firma que el método puede lanzar checked exceptions
// El llamador debe manejarlas o propagarlas a su vez

public void exportarCSV(List<Pedido> pedidos, String ruta) throws IOException {
    try (BufferedWriter bw = new BufferedWriter(new FileWriter(ruta))) {
        for (Pedido p : pedidos) {
            bw.write(p.getId() + "," + p.getTotal());
            bw.newLine();
        }
    }
    // IOException de FileWriter y bw.write() se propaga al llamador
}

// El llamador decide qué hacer:
try {
    exportarCSV(pedidos, "/tmp/export.csv");
} catch (IOException e) {
    mostrarError("No se pudo exportar: " + e.getMessage());
}

Envolver excepciones preservando la causa

Cuando se convierte una checked en unchecked, siempre pasar la excepción original como causa. Si no se hace, se pierde el stack trace original y el diagnóstico de problemas en producción se vuelve mucho más difícil.

// Patrón habitual: envolver checked en unchecked para no contaminar las firmas
// Preservar la excepción original como "causa" para no perder información de debug

public List<String> leerLineas(String ruta) {
    try {
        return java.nio.file.Files.readAllLines(java.nio.file.Paths.get(ruta));
    } catch (IOException e) {
        throw new RuntimeException("Error al leer fichero: " + ruta, e); // e = causa
    }
}

// La causa se puede obtener con getCause()
try {
    leerLineas("/no/existe.txt");
} catch (RuntimeException e) {
    System.out.println("Causa raíz: " + e.getCause()); // IOException original
    e.printStackTrace(); // muestra toda la cadena de causas
}

Diseño de contratos con excepciones

Elige la excepción que mejor describe la violación. IllegalArgumentException para parámetros inválidos, IllegalStateException para estado inconsistente, NullPointerException para parámetros null explícitamente rechazados, y UnsupportedOperationException para operaciones no implementadas.

// Diseñar contratos claros con excepciones

// Precondiciones: IllegalArgumentException, NullPointerException
public Pedido crearPedido(Cliente cliente, List<Producto> productos) {
    Objects.requireNonNull(cliente, "cliente no puede ser null");
    Objects.requireNonNull(productos, "productos no puede ser null");
    if (productos.isEmpty()) throw new IllegalArgumentException("Debe haber al menos un producto");
    // ...
}

// Estado inválido: IllegalStateException
public void confirmar() {
    if (estado != Estado.BORRADOR)
        throw new IllegalStateException("Solo se pueden confirmar pedidos en borrador");
    estado = Estado.CONFIRMADO;
}

// Operación no soportada: UnsupportedOperationException
public void eliminar() {
    throw new UnsupportedOperationException("Los pedidos confirmados no se pueden eliminar");
}
Mensajes de excepción útiles: incluye el valor que causó el problema, no solo el hecho de que fue inválido. "id no puede ser negativo, era -5" es infinitamente más útil en un log de producción que "argumento inválido". Objects.requireNonNull(param, "param no puede ser null") es la forma más concisa de validar nulos con mensaje.

Siguiente apartado → Excepciones personalizadas

Índice de la sección

Índice del curso