El mecanismo try-catch-finally
y try-with-resources
try-catch y multi-catch
Un bloque try puede tener múltiples catch. Se evalúan en orden: se ejecuta el
primer catch cuyo tipo coincida con la excepción lanzada. Coloca los tipos más específicos primero
— un catch (Exception e) antes de uno más específico causaría un error de compilación por código
inalcanzable.
// try-catch básico
try {
int resultado = 10 / 0; // lanza ArithmeticException
String s = null;
s.length(); // lanza NullPointerException
} catch (ArithmeticException e) {
System.err.println("División por cero: " + e.getMessage());
} catch (NullPointerException e) {
System.err.println("Referencia nula: " + e.getMessage());
} catch (Exception e) {
// catch de excepción base: captura cualquier excepción no capturada antes
System.err.println("Error inesperado: " + e.getMessage());
}
// Multi-catch (Java 7+): múltiples excepciones del mismo manejador
try {
// código que puede lanzar varias excepciones no relacionadas
} catch (IOException | SQLException e) {
System.err.println("Error de E/S o BBDD: " + e.getMessage());
} finally
El bloque finally se ejecuta siempre: si el try termina normalmente, si lanza una excepción
o si hay un return. Era el mecanismo de liberación de recursos antes de try-with-resources. Hoy su uso principal es para lógica de limpieza que no está ligada a un AutoCloseable.
// finally: se ejecuta siempre, con o sin excepción
Connection con = null;
try {
con = obtenerConexion();
// operaciones con la conexión...
} catch (SQLException e) {
System.err.println("Error SQL: " + e.getMessage());
} finally {
// garantizado: se ejecuta aunque haya excepción
if (con != null) {
try { con.close(); } catch (SQLException ignored) { }
}
}
// Nota: si finally contiene un return, suprime cualquier excepción lanzada en try
// Evitar return en finally — es un anti-patrón que hace difícil de depurar el código try-with-resources
Cualquier objeto que implemente AutoCloseable puede declararse en el
try. La JVM garantiza que se llama a close() en orden inverso al de declaración, incluso
si se lanza una excepción. Úsalo para todos los recursos que requieren cierre: ficheros, conexiones, streams,
etc.
// try-with-resources (Java 7+): cierre automático de AutoCloseable
// Reemplaza el patrón try-catch-finally anterior — úsalo siempre
try (Connection con = dataSource.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM pedidos WHERE id = ?")) {
ps.setLong(1, pedidoId);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
System.out.println(rs.getString("descripcion"));
}
} // rs se cierra aquí
} catch (SQLException e) {
throw new RuntimeException("Error al buscar pedido " + pedidoId, e);
}
// con y ps se cierran aquí en orden inverso, incluso si hubo excepción Excepciones suprimidas
Si tanto el bloque try como close() lanzan excepciones,
try-with-resources preserva la excepción principal y adjunta la de
close() como excepción suprimida. Con el patrón finally
manual, la excepción de close() sobreescribía y perdía la excepción original.
// Excepciones suprimidas: cuando close() lanza excepción y ya hay una activa
// Java gestiona esto automáticamente en try-with-resources
try (MiRecurso r = new MiRecurso()) {
throw new RuntimeException("excepción principal");
// r.close() también lanza excepción
} catch (RuntimeException e) {
System.out.println("Principal: " + e.getMessage());
for (Throwable suprimida : e.getSuppressed()) {
System.out.println("Suprimida: " + suprimida.getMessage());
}
}
// Antes de Java 7 con finally manual, la excepción de close() sobreescribía la principal