Java try-with-resources
Cierra recursos AutoCloseable automáticamente en Java con la sentencia try-with-resources.
La sentencia try-with-resources es la solución de Java al patrón "ábrelo, úsalo, siempre ciérralo". Declaras un recurso al inicio del try, y la JVM garantiza que se cerrará cuando el bloque termine — ya sea que el bloque retorne normalmente, lance una excepción o sea abandonado por algún otro flujo de control. El verboso baile de finally se colapsa en una sola declaración.
Este capítulo cubre la sintaxis, qué califica como recurso, cómo se cierran múltiples recursos, el mecanismo de excepciones suprimidas que hace que la funcionalidad sea confiable, y cuándo no usarla.
La forma
try (FileReader reader = new FileReader(path)) {
// use reader
}Eso es todo. Sin finally, sin close() explícito. Cuando el try termina, la JVM llama a reader.close() automáticamente. Cualquier cláusula catch y finally que agregues sigue funcionando — se ejecutan después de que el recurso es cerrado.
try (FileReader reader = new FileReader(path)) {
// use reader
} catch (IOException e) {
// handle — at this point the reader is already closed
}Qué puede ser un recurso
Un recurso debe implementar AutoCloseable (o su subinterfaz más antigua Closeable). Esa interfaz declara un único método:
public interface AutoCloseable {
void close() throws Exception;
}La mayoría de los tipos que usarías ya lo implementan: InputStream, OutputStream, Reader, Writer, Connection, Statement, ResultSet, Scanner, envoltorios de Lock y muchos clientes de terceros. Si estás escribiendo una clase que posee un recurso, tú mismo implementas AutoCloseable.
Múltiples recursos
Separa las declaraciones con punto y coma. Los recursos se cierran en orden inverso de declaración, por lo que el último abierto es el primero en cerrarse:
try (
FileInputStream in = new FileInputStream(src);
FileOutputStream out = new FileOutputStream(dst)
) {
in.transferTo(out);
}
// closes `out` first, then `in`El orden inverso importa porque el segundo recurso a menudo depende del primero. Cerrar primero el dependiente deja la base intacta por un momento más, lo cual es el orden más seguro.
Reutilizar recursos existentes (Java 9+)
Si ya tienes una variable que contiene un AutoCloseable, no necesitas declarar uno nuevo — pásalo por nombre:
BufferedReader reader = openSomewhere();
try (reader) {
// use it
}La variable debe ser final o efectivamente final (no puedes reasignarla). Esta forma es útil cuando un recurso se construye en otro lugar — a costa de hacer el cierre menos visible. Úsala cuando la variable existente está justo ahí; de lo contrario, prefiere la declaración en línea.
Excepciones suprimidas
Aquí está la parte sutil. ¿Qué ocurre si el cuerpo del try lanza una excepción y la llamada a close() también lanza? Antes de Java 7, la excepción de cierre del finally reemplazaría a la original — y perderías la causa real.
try-with-resources resuelve esto. La excepción original es la que se lanza al llamador; cualquier excepción en tiempo de cierre se adjunta a ella como excepciones suprimidas, recuperables con Throwable.getSuppressed():
try (Resource r = new Resource()) {
r.use(); // throws A
} // close() throws B
// caller sees A
// A.getSuppressed() returns [B]El printStackTrace() predeterminado también imprime las excepciones suprimidas (líneas Suppressed: ... bajo la principal). Casi nunca tienes que manejar esto manualmente — el lenguaje preserva la cadena por ti.
Una forma real típica
Un patrón común, poniendo todo junto:
public List<String> readAllLines(Path p) throws IOException {
try (BufferedReader reader = Files.newBufferedReader(p)) {
return reader.lines().toList();
}
}Cosas a notar: sin cierre explícito, sin finally, el cuerpo puede hacer return directamente, y el llamador sigue obteniendo un IOException limpio si algo falló.
Cuándo try-with-resources es la herramienta incorrecta
Es la herramienta correcta cuando posees el ciclo de vida completo del recurso durante un bloque. Es incorrecta cuando:
- El recurso vive más que el bloque — por ejemplo, una conexión almacenada en caché entre llamadas. Cerrarlo al final del método rompería al siguiente usuario.
- No eres el dueño del recurso — un llamador te lo entregó. Ellos lo cerrarán.
En esos casos, deja el cierre al propietario. Si no puedes determinar quién es el propietario, eso es un olor a diseño deficiente — averígualo antes de escribir el código.
Un ejemplo trabajado
Un programa corto que copia una cadena a una tubería en memoria y la lee de vuelta a través de un lector con búfer. Ambos flujos son AutoCloseable; ambos se cierran automáticamente; agregamos un pequeño recurso personalizado para que puedas ver el orden de cierre en la salida.
En la salida puedes ver que el orden de declaración es a, b, src, buf pero el orden de cierre es inverso: buf, src, b, a. Esa es la garantía del lenguaje — primero abierto, último cerrado.
Qué sigue
Hasta ahora solo hemos capturado excepciones que Java nos lanzó. El código real también necesita generar las propias — para argumentos inválidos, invariantes rotos o fallos de dominio. Continúa con Java throw y throws.