W3docs

Java try...catch

Gérez les erreurs d'exécution en Java avec les blocs try...catch pour maintenir votre programme en marche quand quelque chose tourne mal.

try/catch est la plus petite unité de gestion des exceptions en Java. Vous placez le code risqué dans try, et vous le faites suivre d'un ou plusieurs blocs catch qui indiquent quoi faire si un type d'exception particulier est levé. Ensemble, ils forment une instruction unique ; vous ne pouvez pas avoir l'un sans l'autre (ni sans finally, que nous verrons dans un chapitre ultérieur).

Anatomie

try {
  // code that might throw
} catch (ExceptionType e) {
  // code that runs only if a matching exception was thrown
}

Quelques points faciles à manquer au premier coup d'œil :

  • La variable dans catch (e par convention) a une portée limitée au bloc catch. Vous pouvez lui donner n'importe quel nom ; e et ex sont les choix habituels.
  • Le paramètre catch est effectivement final — vous pouvez le lire mais vous ne devriez pas le réaffecter. Le traiter comme immuable rend le code plus facile à suivre.
  • Le bloc try est son propre scope. Les variables déclarées à l'intérieur ne sont pas visibles dans catch ni après l'instruction. Si vous avez besoin d'une valeur calculée dans try plus tard, déclarez-la à l'extérieur.
String content;
try {
  content = Files.readString(path);
} catch (IOException e) {
  content = "";   // works because `content` was declared outside the try
}
System.out.println(content);

Quels catch correspondent

Un bloc catch (T e) s'exécute lorsque l'exception levée est une instance de T — y compris tout sous-type de T. Donc :

  • catch (Exception e) capture presque tout : IOException, NullPointerException, vos propres exceptions personnalisées.
  • catch (RuntimeException e) capture les bugs d'exécution mais pas les exceptions vérifiées comme IOException.
  • catch (NullPointerException e) capture uniquement les NPE (et les sous-classes, dont il n'y en a généralement aucune).

À l'intérieur du try, seul le premier catch correspondant s'exécute. Si vous listez un type plus large avant un type plus étroit, le catch plus étroit est inaccessible et le compilateur refuse :

try { ... }
catch (Exception e)        { ... }   // matches everything
catch (IOException e)      { ... }   // ERROR: unreachable

Listez toujours les catch du plus spécifique au plus général.

Que faire dans un catch

Un bloc catch n'est pas un endroit pour faire disparaître les exceptions. C'est un endroit pour décider quoi faire face à un échec connu. Les options réalistes sont :

  • Récupérer — réessayer, revenir à une valeur par défaut, basculer vers une ressource différente. C'est le meilleur cas, et c'est plus rare que vous ne le pensez.
  • Journaliser et relancer — enregistrer les détails et laisser l'exception se propager vers un gestionnaire de niveau supérieur qui sait quoi faire.
  • Encapsuler et relancer — traduire une exception de bas niveau en une exception qui correspond au vocabulaire de cette couche (IOExceptionConfigLoadException).
  • Traduire en valeur de retour — quand l'échec est vraiment attendu (par ex. lors de l'analyse de la saisie utilisateur), retourner Optional.empty() ou une valeur sentinelle.

Ce qu'il ne faut pas faire, c'est capturer une exception et continuer silencieusement sans la journaliser ni agir dessus. C'est ainsi que les bugs disparaissent dans l'air. Nous y reviendrons dans le chapitre sur les bonnes pratiques.

Lire les informations d'une exception

Le paramètre catch est un objet. Trois méthodes que vous utiliserez constamment :

  • e.getMessage() — la description lisible par l'humain. Peut être null.
  • e.toString() — nom de la classe plus le message, par ex. java.io.IOException: file not found.
  • e.printStackTrace() — écrit la trace vers System.err. Pratique pendant le débogage, mais utilisez un vrai logger en production (il achemine la trace par le même canal que tout le reste).

Une quatrième, e.getCause(), renvoie l'exception sous-jacente lorsque l'une d'elles a été encapsulée — utile lorsque vous devez comprendre l'échec original à l'intérieur d'une couche de traduction.

Capturer Exception vs. capturer Throwable

Vous pouvez écrire catch (Throwable t) et cela capturera tout — y compris les Errors comme OutOfMemoryError. Ne le faites pas. Les Errors signifient que la JVM est en difficulté et que votre code n'est pas en mesure d'y réagir sensiblement. Capturer Throwable masque des bugs qui devraient faire planter le processus.

Restez sur Exception ou l'un de ses sous-types. Si vous avez vraiment besoin de journaliser l'inattendu avant de le laisser mourir, la bonne forme est catch (RuntimeException e) { log; throw; }.

Un exemple concret

Un petit utilitaire qui analyse un entier à partir de chaque ligne d'entrée. Certaines lignes sont des nombres valides, d'autres non, et l'une est null. Nous utilisons try/catch pour continuer malgré les mauvaises lignes et comptabiliser ce qui s'est passé. L'appel de line.trim() lève une NullPointerException sur l'entrée null, tandis qu'Integer.parseInt lève une NumberFormatException sur tout ce qui n'est pas un entier — deux types d'exceptions distincts, chacun avec son propre catch.

java— editable, runs on the server

La sortie est :

not an integer: "hello"
null line — skipped
not an integer: "3.14"
---
parsed: 3, failed: 3, sum: 118

Deux choses à remarquer. Premièrement, le programme se termine. Sans les catch, la première mauvaise ligne mettrait fin à l'exécution et rien après elle ne serait comptabilisé. Deuxièmement, les deux échecs sont distingués : la ligne null prend la branche NullPointerException et affiche son propre message, tandis que "hello" et "3.14" prennent la branche NumberFormatException. Sélectionner les catch par type est la façon dont vous empêchez différents échecs de se confondre.

La suite

Un seul catch est le cas le plus simple. Le vrai code doit généralement gérer plusieurs échecs différents à la fois. Continuez vers les blocs catch multiples et le multi-catch en Java. Ensuite, le bloc finally et try-with-resources complètent la syntaxe, tandis que throw et throws couvrent l'autre côté : signaler les échecs depuis votre propre code.

Pratique

Pratique
Un bloc try exécute `a[5] = 1;` sur un tableau à 2 éléments, avec `catch (RuntimeException e)` écrit en premier et `catch (ArrayIndexOutOfBoundsException e)` écrit en second. Que se passe-t-il ?
Un bloc try exécute `a[5] = 1;` sur un tableau à 2 éléments, avec `catch (RuntimeException e)` écrit en premier et `catch (ArrayIndexOutOfBoundsException e)` écrit en second. Que se passe-t-il ?
Was this page helpful?