Excepciones y validación
Un dato previsible puede validarse con una condición. Una excepción comunica que el flujo normal no puede continuar como estaba previsto.
Validar o lanzar
if (cantidad <= 0) {
System.out.println("La cantidad debe ser positiva");
}
No uses excepciones para sustituir un if sencillo. En cambio, convertir texto puede fallar aunque hayamos recibido una entrada:
try {
int edad = Integer.parseInt("veinte");
} catch (NumberFormatException error) {
System.out.println("La edad debe ser numérica");
}
Jerarquía básica
Exception agrupa muchas situaciones recuperables. Las checked exceptions deben capturarse o declararse con throws; IOException es un ejemplo. Las unchecked exceptions heredan de RuntimeException, como NumberFormatException o IndexOutOfBoundsException, y suelen señalar datos inesperados o errores de programación.
Que una excepción sea unchecked no significa que deba ignorarse.
Capturar con un propósito
try {
int posicion = Integer.parseInt(texto);
System.out.println(nombres.get(posicion));
} catch (NumberFormatException error) {
System.out.println("La posición no es un número");
} catch (IndexOutOfBoundsException error) {
System.out.println("La posición no existe");
}
Varios catch permiten respuestas específicas. Un multi-catch es útil si la recuperación es idéntica: catch (NumberFormatException | IndexOutOfBoundsException error).
Evita catch (Exception e) {}: silencia el problema y dificulta diagnosticarlo. Captura cuando puedas recuperarte, traducir el error, añadir contexto o comunicarlo correctamente; si no, propagar suele ser mejor.
throw y throws
throw lanza una excepción concreta. throws declara que un método puede propagarla:
public static int leerCantidad(String texto) {
int cantidad = Integer.parseInt(texto);
if (cantidad < 0) {
throw new IllegalArgumentException("Cantidad negativa");
}
return cantidad;
}
Las excepciones unchecked pueden propagarse aunque no aparezcan en throws. Es legal declararlas, pero normalmente no es necesario. El siguiente ejemplo, en cambio, usa SaldoInsuficienteException: al ser checked, debe capturarse o declararse.
Excepción de dominio
class SaldoInsuficienteException extends Exception {
public SaldoInsuficienteException(String mensaje) {
super(mensaje);
}
}
class Cuenta {
private double saldo;
public Cuenta(double saldo) { this.saldo = saldo; }
public void retirar(double cantidad) throws SaldoInsuficienteException {
if (cantidad <= 0) throw new IllegalArgumentException("Cantidad no positiva");
if (cantidad > saldo) {
throw new SaldoInsuficienteException("Saldo disponible: " + saldo);
}
saldo -= cantidad;
}
}
La cantidad no positiva incumple el contrato del método; el saldo insuficiente es una situación de dominio que el llamador puede gestionar.
finally
El bloque finally se ejecuta normalmente haya o no excepción y se utilizaba para liberar recursos:
try {
System.out.println("Operación");
} finally {
System.out.println("Limpieza final");
}
En JV3-06 aprenderemos try-with-resources, que evita cierres manuales y expresa mejor esa responsabilidad.
Ejemplo completo
class SaldoInsuficienteException extends Exception {
public SaldoInsuficienteException(String mensaje) { super(mensaje); }
}
class Cuenta {
private double saldo;
public Cuenta(double saldo) { this.saldo = saldo; }
public void retirar(double cantidad) throws SaldoInsuficienteException {
if (cantidad <= 0) throw new IllegalArgumentException("Cantidad no positiva");
if (cantidad > saldo) throw new SaldoInsuficienteException("Saldo insuficiente");
saldo -= cantidad;
}
}
public class ExcepcionesDemo {
public static void main(String[] args) {
Cuenta cuenta = new Cuenta(50);
try {
cuenta.retirar(70);
} catch (SaldoInsuficienteException error) {
System.out.println(error.getMessage());
}
try {
System.out.println(Integer.parseInt("12"));
} catch (NumberFormatException error) {
System.out.println("Número inválido");
} finally {
System.out.println("Fin del intento");
}
}
}
Errores habituales
- Usar excepciones para condiciones esperadas fáciles de validar.
- Capturar demasiado arriba o demasiado pronto.
- Silenciar errores.
- Lanzar una excepción sin mensaje útil.
- Confundir una regla de dominio con un fallo del programador.
Practica: cupo agotado
Enunciado y solución
Crea una checked exception CupoAgotadoException. Un método reservar(int libres) la lanza si no quedan plazas. En main, captura el error e informa al usuario.
class CupoAgotadoException extends Exception {
public CupoAgotadoException() { super("No quedan plazas"); }
}
public class ReservasDemo {
static void reservar(int libres) throws CupoAgotadoException {
if (libres <= 0) throw new CupoAgotadoException();
System.out.println("Reserva creada");
}
public static void main(String[] args) {
try { reservar(0); }
catch (CupoAgotadoException error) { System.out.println(error.getMessage()); }
}
}
La captura está en el límite que sabe comunicar el resultado; la regla permanece cerca de la operación.