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(epar convention) a une portée limitée au bloc catch. Vous pouvez lui donner n'importe quel nom ;eetexsont 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
tryest son propre scope. Les variables déclarées à l'intérieur ne sont pas visibles danscatchni après l'instruction. Si vous avez besoin d'une valeur calculée danstryplus 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 commeIOException.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: unreachableListez 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 (
IOException→ConfigLoadException). - 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 êtrenull.e.toString()— nom de la classe plus le message, par ex.java.io.IOException: file not found.e.printStackTrace()— écrit la trace versSystem.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.
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.