W3docs

Java try-with-resources

Fermez automatiquement les ressources AutoCloseable en Java avec l'instruction try-with-resources.

L'instruction try-with-resources est la solution Java au schéma « ouvrir, utiliser, toujours fermer ». Vous déclarez une ressource en haut du try, et la JVM garantit qu'elle sera fermée à la sortie du bloc — que le bloc se soit terminé normalement, ait lancé une exception, ou ait été abandonné par un autre flux de contrôle. La verbeuse danse finally s'effondre en une simple déclaration.

Ce chapitre couvre la syntaxe, ce qui qualifie une ressource, comment plusieurs ressources sont fermées, le mécanisme des exceptions supprimées qui rend la fonctionnalité fiable, et quand ne pas l'utiliser.

La forme

try (FileReader reader = new FileReader(path)) {
  // use reader
}

C'est tout. Pas de finally, pas de close() explicite. Quand le try se termine, la JVM appelle reader.close() automatiquement. Les clauses catch et finally que vous ajoutez fonctionnent toujours — elles s'exécutent après la fermeture de la ressource.

try (FileReader reader = new FileReader(path)) {
  // use reader
} catch (IOException e) {
  // handle — at this point the reader is already closed
}

Ce qui peut être une ressource

Une ressource doit implémenter AutoCloseable (ou son ancienne sous-interface Closeable). Cette interface déclare une seule méthode :

public interface AutoCloseable {
  void close() throws Exception;
}

La plupart des types que vous utiliseriez l'implémentent déjà : InputStream, OutputStream, Reader, Writer, Connection, Statement, ResultSet, Scanner, les wrappers Lock, et de nombreux clients tiers. Si vous écrivez une classe qui possède une ressource, vous implémentez AutoCloseable vous-même.

Plusieurs ressources

Séparez les déclarations par des points-virgules. Les ressources sont fermées dans l'ordre inverse de déclaration, donc la dernière ouverte est la première fermée :

try (
  FileInputStream  in  = new FileInputStream(src);
  FileOutputStream out = new FileOutputStream(dst)
) {
  in.transferTo(out);
}
// closes `out` first, then `in`

L'ordre inverse est important car la deuxième ressource dépend souvent de la première. Fermer la ressource dépendante en premier laisse la fondation intacte un instant de plus, ce qui est l'ordre le plus sûr.

Réutilisation de ressources existantes (Java 9+)

Si vous avez déjà une variable contenant un AutoCloseable, vous n'avez pas besoin d'en déclarer une nouvelle — passez-la par son nom :

BufferedReader reader = openSomewhere();
try (reader) {
  // use it
}

La variable doit être final ou effectivement final (vous ne pouvez pas la réassigner). Cette forme est utile quand une ressource est construite ailleurs — au prix de rendre la fermeture moins visible. Utilisez-la quand la variable existante est juste là ; sinon, préférez la déclaration en ligne.

Exceptions supprimées

Voici la partie subtile. Que se passe-t-il si le corps du try lance une exception et que l'appel à close() lance également une exception ? Avant Java 7, l'exception de fermeture du finally remplaçait l'originale — et vous perdiez la vraie cause.

try-with-resources résout ce problème. L'exception originale est celle lancée à l'appelant ; toute exception survenant lors de la fermeture est attachée à elle en tant qu'exception supprimée, récupérable avec Throwable.getSuppressed() :

try (Resource r = new Resource()) {
  r.use();          // throws A
}                   // close() throws B
// caller sees A
// A.getSuppressed() returns [B]

La méthode printStackTrace() par défaut affiche également les exceptions supprimées (lignes Suppressed: ... sous la principale). Vous n'avez presque jamais à gérer cela manuellement — le langage préserve la chaîne pour vous.

Une forme réelle typique

Un schéma courant, réunissant tout :

public List<String> readAllLines(Path p) throws IOException {
  try (BufferedReader reader = Files.newBufferedReader(p)) {
    return reader.lines().toList();
  }
}

Points à noter : pas de fermeture explicite, pas de finally, le corps peut faire un return directement, et l'appelant reçoit toujours une IOException propre si quelque chose a échoué.

Quand try-with-resources n'est pas le bon outil

C'est le bon outil quand vous possédez toute la durée de vie de la ressource pour un seul bloc. C'est le mauvais outil quand :

  • La ressource vit plus longtemps que le bloc — par exemple, une connexion mise en cache entre les appels. La fermer à la fin de la méthode casserait l'utilisateur suivant.
  • Vous ne possédez pas la ressource — un appelant vous l'a passée. C'est lui qui la fermera.

Dans ces cas, laissez la fermeture au propriétaire. Si vous ne savez pas qui est le propriétaire, c'est une odeur de conception — clarifiez cela avant d'écrire le code.

Un exemple complet

Un court programme qui copie une chaîne dans un pipe en mémoire et la relit via un buffered reader. Les deux flux sont AutoCloseable ; les deux sont fermés automatiquement ; nous ajoutons une petite ressource personnalisée pour que vous puissiez voir l'ordre de fermeture dans la sortie.

java— editable, runs on the server

Dans la sortie, vous pouvez voir que l'ordre de déclaration est a, b, src, buf mais que l'ordre de fermeture est inverse : buf, src, b, a. C'est la garantie du langage — le premier ouvert est le dernier fermé.

Et ensuite

Jusqu'à présent, nous avons seulement intercepté les exceptions que Java nous lançait. Le code réel doit aussi en lever lui-même — pour les arguments invalides, les invariants brisés, ou les échecs de domaine. Continuez vers Java throw et throws.

Pratique

Pratique
Un bloc `try-with-resources` ouvre deux ressources `AutoCloseable`. Le corps lance une `IOException`, et le `close()` de la deuxième ressource lance également une `IOException`. Qu'est-ce que l'appelant voit ?
Un bloc `try-with-resources` ouvre deux ressources `AutoCloseable`. Le corps lance une `IOException`, et le `close()` de la deuxième ressource lance également une `IOException`. Qu'est-ce que l'appelant voit ?
Was this page helpful?