W3docs

Bloc finally en Java

Exécutez du code de nettoyage en Java avec les blocs finally qui s'exécutent toujours, qu'une exception ait été levée ou non.

Un bloc finally s'exécute quelle que soit la façon dont le try se termine — fin normale, exception interceptée, exception non interceptée, ou même un return anticipé. Cette garantie est sa raison d'être : c'est là que vous placez le code de nettoyage qui doit s'exécuter, absolument. Fermer un descripteur de fichier, libérer un verrou, restaurer l'état d'un thread — tout ce dont l'absence laisserait le programme dans un état pire que celui dans lequel il se trouvait.

La structure

try {
  // risky code
} catch (SomeException e) {
  // optional — zero or more catches
} finally {
  // always runs after the try (and any matching catch)
}

Vous pouvez associer finally à catch, sans aucun catch (try { ... } finally { ... }), ou avec plusieurs catches. Les éléments se combinent librement.

Ce que signifie « s'exécute toujours »

Un bloc finally s'exécute quand le contrôle quitte le try, quelle qu'en soit la raison :

  • Fin normale du bloc tryfinally s'exécute après la dernière instruction.
  • Une exception levée depuis tryfinally s'exécute après la fin du catch correspondant (ou, si aucun catch ne correspond, juste avant que l'exception se propage).
  • return dans try ou catchfinally s'exécute avant que le retour prenne réellement effet.
  • break ou continue qui ferait sortir du tryfinally s'exécute avant le saut.

Les seules façons de contourner finally sont : la JVM elle-même meurt (System.exit, une coupure de courant, Runtime.halt), une boucle infinie ou un deadlock dans le try, ou Thread.stop (déprécié pour cette raison précise). Pour tout ce que vous écrivez dans du code applicatif normal, finally est une garantie absolue.

try {
  return computeAnswer();    // even though there's a return here,
} finally {
  cleanup();                 // this runs before the method actually returns
}

À quoi sert finally

La réponse honnête est : le nettoyage des ressources, presque toujours. Avant que Java 7 n'introduise try-with-resources, la forme canonique était :

InputStream in = null;
try {
  in = new FileInputStream(path);
  // read from in...
} catch (IOException e) {
  // handle
} finally {
  if (in != null) {
    try { in.close(); } catch (IOException ignored) { /* */ }
  }
}

Ce try/catch imbriqué autour de close() dans le finally est le type de bruit que try-with-resources a été conçu pour éliminer. Nous le verrons dans le prochain chapitre. Mais comprendre à quoi finally sert permet de mieux comprendre la nouvelle construction.

Au-delà des ressources, finally est utile pour :

  • Restaurer l'état partagé que vous avez modifié le temps du try — incrémenter un compteur de profondeur, basculer un indicateur, échanger un ThreadLocal.
  • Libérer des verrous acquis manuellement (Lock.lock()try { ... } finally { lock.unlock(); }).
  • Arrêter des minuteries ou fermer des transactions qui n'implémentent pas AutoCloseable.

Ce à quoi finally ne sert pas

N'écrivez pas de logique produisant des résultats dans finally. Le bloc s'exécute quelle que soit l'issue — il ne sait pas si le try a réussi. Si vous mettez commit() dans finally, vous validerez même en cas d'échec.

Et ne faites pas de return depuis finally. C'est l'un des coins véritablement dangereux du langage :

try {
  return 1;
} finally {
  return 2;     // wins — the method returns 2 and the original return is lost
}

Le return (ou throw) dans finally écrase tout retour ou exception provenant du try. L'exception qui allait se propager est silencieusement ignorée. La plupart des outils d'analyse statique signalent cela comme une erreur pour cette raison. La règle : finally fait du nettoyage ; finally ne produit pas de valeurs.

Ordre d'exécution

Quand un catch et un finally sont tous deux présents :

try   { ... }
catch { ... }
finally { ... }

L'ordre est exactement celui que vous imaginez : try s'exécute, si une exception est levée et qu'un catch correspond ce catch s'exécute, et finally s'exécute après celui des deux qui s'est produit. Si finally lève ensuite une exception, ce nouveau throw remplace ce qui se propageait depuis le try ou le catch — une autre raison de garder finally discret.

Un exemple concret

Nous instrumentons une petite « transaction » avec try/catch/finally et l'appelons de trois façons différentes : succès normal, échec récupérable, et échec irrécupérable. Le finally s'exécute dans les trois cas, ce qui est tout l'intérêt.

java— editable, runs on the server

Dans le troisième appel, doWork lève une RuntimeException que le catch local ne correspond pas. Le finally s'exécute quand même et affiche « release lock » avant que l'exception continue de remonter jusqu'à main. C'est la propriété que vous souhaitez d'un code de nettoyage — il ne dépend pas du succès ou de l'échec du traitement.

Et ensuite

La forme « ouvrir une ressource, travailler avec, la fermer dans finally » est si courante que Java a créé une instruction dédiée. Continuez vers Java try-with-resources.

Pratique

Pratique
Que retourne cette méthode ?\n\n```java\nstatic int demo() {\n try {\n return 1;\n } finally {\n return 2;\n }\n}\n```
Que retourne cette méthode ?\n\n```java\nstatic int demo() {\n try {\n return 1;\n } finally {\n return 2;\n }\n}\n```
Was this page helpful?