Serie 0x3 - Manejo de Excepciones¶
0x3000 - No atajar la excepción si no es posible tomar una decisión¶
Explicación¶
Si solo vas a loggear o relanzar sin agregar valor, dejá que la excepción se propague naturalmente.
Incorrecto ❌:
try {
procesarArchivo();
} catch (IOException e) {
// Solo loggear y relanzar
logger.error("Error: " + e.getMessage());
throw e; // ❌ No agrega valor
}Correcto ✅:
// ✅ Dejar propagar - agregar throws en firma
public void procesar() throws IOException {
procesarArchivo(); // Propaga naturalmente
}0x3001 - El main de un programa no debe dejar pasar excepciones checked¶
Explicación¶
El main debe manejar todas las excepciones checked y proporcionar mensajes de error apropiados al usuario final.
Incorrecto ❌:
public static void main(String[] args) throws Exception { // ❌
// código
}Correcto ✅:
public static void main(String[] args) {
try {
ejecutarPrograma();
} catch (IOException e) {
System.err.println("Error de archivo: " + e.getMessage());
System.exit(1);
}
}0x3002 - Qué familia de excepciones se eligió debe estar documentada¶
Explicación¶
Documentar en el paquete o clase base por qué se usa checked vs unchecked para las excepciones del dominio.
/**
* Excepciones del dominio de Facturación.
* <p>
* Se usan excepciones UNCHECKED porque:
* - Los errores son de programación (precondiciones violadas)
* - No se espera recuperación en tiempo de ejecución
*/
package ar.unrn.facturacion.excepciones;0x3003 - No atajar una excepción lanzada en el mismo bloque¶
Explicación¶
Si lanzás una excepción dentro de un try y la atajás en el mismo catch, usá if-else en su lugar.
Incorrecto ❌:
try {
if (invalido) {
throw new IllegalArgumentException();
}
} catch (IllegalArgumentException e) {
// manejar
}Correcto ✅:
if (invalido) {
// manejar directamente
} else {
// flujo normal
}0x3004 - No convertir excepciones checked a unchecked sin justificación¶
Explicación¶
No atajar una excepción checked (IOException, SQLException) y relanzar una excepción unchecked genérica perdiendo información del tipo original.
Incorrecto ❌:
try {
leerArchivo();
} catch (IOException e) {
throw new RuntimeException("Error"); // ❌ Pérdida de información
}Correcto ✅:
try {
leerArchivo();
} catch (IOException e) {
throw new ArchivoNoDisponibleException("No se pudo leer: " + archivo, e);
}0x3005 - Sean específicos con lo que atajan, no está permitido atajar Exception o RuntimeException¶
Explicación¶
Atajar excepciones específicas, no genéricas. Esto permite manejar cada caso apropiadamente.
Incorrecto ❌:
try {
// código
} catch (Exception e) { // ❌ Demasiado genérico
// manejar
}Correcto ✅:
try {
// código
} catch (IOException e) {
// manejar IO
} catch (SQLException e) {
// manejar BD
}0x3006 - Situaciones diferentes requieren excepciones diferentes¶
Explicación¶
Situaciones como “arreglo vacío” y “arreglo null” son casos diferentes que ameritan mensajes y tipos de excepciones distintos.
Incorrecto ❌:
if (arreglo == null || arreglo.length == 0) {
throw new IllegalArgumentException("Arreglo inválido");
}Correcto ✅:
if (arreglo == null) {
throw new NullPointerException("El arreglo no puede ser null");
}
if (arreglo.length == 0) {
throw new IllegalArgumentException("El arreglo no puede estar vacío");
}Ejemplo reutilizable (TP3 - Arreglos):
/**
* Verifica que un arreglo no sea null ni esté vacío.
* @param arreglo el arreglo a verificar
* @throws NullPointerException si el arreglo es null
* @throws IllegalArgumentException si el arreglo está vacío
*/
private static void validarArreglo(int[] arreglo) {
if (arreglo == null) {
throw new NullPointerException("El arreglo no puede ser null");
}
if (arreglo.length == 0) {
throw new IllegalArgumentException("El arreglo no puede estar vacío");
}
}0x3007 - ‘Largo cero’ y null son dos situaciones bastante diferentes¶
Explicación¶
Que requieren de excepciones distintas para que su tratamiento pueda ser más específico. Ver 0x3006 - Situaciones diferentes requieren excepciones diferentes.
0x3008 - Declarar el lanzamiento de una excepción no controlada es un error¶
Explicación¶
No es correcto (ni necesario) declarar throws para RuntimeException y sus subclases.
Incorrecto ❌:
public void metodo() throws RuntimeException { // ❌ Innecesario
// código
}Correcto ✅:
public void metodo() { // ✅ RuntimeException no se declara
// código
}0x3009 - No está permitido lanzar excepciones base: Exception o RuntimeException¶
Explicación¶
Lanzar excepciones específicas del dominio o estándar de Java, no las clases base.
Incorrecto ❌:
throw new Exception("error");
throw new RuntimeException("error");Correcto ✅:
throw new MiExcepcionEspecifica("error");
throw new IllegalArgumentException("parámetro inválido");0x300A - Mejor prevenir que atajar¶
Explicación¶
Siempre que sea posible, prevenir la excepción en lugar de esperar a que falle (LBYL - Look Before You Leap).
Menos óptimo ⚠️:
try {
int resultado = dividir(a, b);
} catch (ArithmeticException e) {
// manejar división por cero
}Mejor ✅:
if (b != 0) {
int resultado = dividir(a, b);
} else {
// manejar caso especial
}0x300B - Silenciar una excepción no es la forma de gestionarla¶
Explicación¶
No dejar bloques catch vacíos. Como mínimo, loggear el error.
Incorrecto ❌:
try {
operacionRiesgosa();
} catch (Exception e) {
// ❌ Bloque vacío - se silencia el error
}Correcto ✅:
try {
operacionRiesgosa();
} catch (Exception e) {
logger.error("Error en operación riesgosa", e);
// Y tomar decisión: reintentar, valor por defecto, etc.
}0x300C - No está permitido atajar para relanzar sin agregar información útil¶
Explicación¶
Si solo envolvés la excepción sin agregar contexto, dejá que se propague.
Incorrecto ❌:
try {
leerArchivo();
} catch (IOException e) {
throw new IOException(e); // ❌ Solo envuelve, no agrega valor
}Correcto ✅:
try {
leerArchivo();
} catch (IOException e) {
throw new ArchivoConfiguracionException(
"No se pudo leer configuración de: " + archivo, e);
}0x300D - Atajar para hacer algún tipo de print no es gestionar la excepción¶
Explicación¶
Imprimir el stack trace no es manejar la excepción. Usar logging apropiado y tomar decisión sobre cómo continuar.
Incorrecto ❌:
try {
operacion();
} catch (Exception e) {
e.printStackTrace(); // ❌ Solo imprime, no maneja
}Correcto ✅:
try {
operacion();
} catch (OperacionException e) {
logger.error("Error en operación", e);
// Reintentar, usar valor por defecto, o relanzar
}