Capturer une exception au lieu de planter
Vous savez déjà que lorsqu'un problème survient, Java lance une exception, et qu'une exception non gérée arrête votre programme avec une stack trace. Un bloc try-catch est votre façon de reprendre le contrôle : vous entourez le code risqué d'un try, et s'il lance une exception, Java saute au bloc catch correspondant au lieu de planter.
Au moment où 10 / 0 lance l'exception, le reste du bloc try est ignoré et le contrôle passe au catch. Une fois le catch terminé, l'exécution se poursuit normalement : le programme ne meurt pas.
La variable du catch contient l'exception
Le e dans catch (ArithmeticException e) est un véritable objet. Il porte des informations sur ce qui a mal tourné, le plus utile étant un message :
e.getMessage() renvoie une courte description. Pendant le débogage, e.printStackTrace() affiche la trace complète montrant exactement d'où vient l'exception ; utilisez-le quand un message seul ne suffit pas à trouver la cause.
Capturez le type précis, pas tout
Un bloc catch ne capture que les exceptions qui correspondent à son type déclaré (ou à une sous-classe). La plus grande erreur de débutant est de capturer Exception pour faire « disparaître » un problème :
// Ne faites pas ça - cela cache de vrais bugs
try {
doWork();
} catch (Exception e) {
// avale NullPointerException, fautes de frappe, erreurs de logique... tout
}
Capturez le type le plus restreint que vous pouvez réellement gérer. Si vous attendez une saisie numérique invalide, capturez NumberFormatException. Tout ce que vous n'avez pas anticipé devrait pouvoir se propager pour que vous en soyez réellement informé au lieu de continuer silencieusement dans un état corrompu.
Gérer plusieurs types d'exceptions
Vous pouvez empiler plusieurs blocs catch. Java les vérifie de haut en bas et exécute le premier qui correspond ; ordonnez-les donc du plus précis au plus général :
Lorsque deux types partagent le même traitement, utilisez un seul multi-catch avec | plutôt que de dupliquer le bloc :
Un piège : un type plus général doit venir après les types précis. Mettre catch (Exception e) en premier rend les blocs plus précis qui suivent inaccessibles, et le compilateur le refuse.
finally s'exécute toujours
Un bloc finally s'exécute après le try et tout catch, quoi qu'il se soit passé : succès, exception capturée ou même return anticipé. C'est là qu'appartient le nettoyage qui doit toujours avoir lieu.
« Closing resource » s'affiche que l'exception se déclenche ou non. Évitez toutefois de faire un return depuis l'intérieur d'un finally : cela peut discrètement écarter une exception ou remplacer une valeur renvoyée par le bloc try.
try-with-resources ferme les choses à votre place
Quand vous travaillez avec quelque chose qui doit être fermé - un fichier, une connexion réseau, une instruction de base de données - le déclarer à l'intérieur de try (...) le ferme automatiquement à la fin du bloc, même si une exception est lancée. Tout type qui implémente l'interface AutoCloseable fonctionne.
Cela remplace l'ancien modèle qui consistait à ouvrir dans le try et fermer dans le finally, et c'est moins source d'erreurs car vous ne pouvez pas oublier de fermer. Préférez-le chaque fois que vous manipulez une ressource fermable.
N'utilisez pas les exceptions pour le flux de contrôle normal
try-catch est fait pour les situations exceptionnelles, pas pour les conditions ordinaires. Capturer une exception coûte plus cher qu'une simple vérification, et rend le code plus difficile à lire. Si vous pouvez tester la condition au préalable, faites-le :
// À éviter : utiliser un catch pour tester une clé manquante
try {
process(map.get(key).trim());
} catch (NullPointerException e) {
// gérer la clé manquante
}
// À préférer : vérifier explicitement
String value = map.get(key);
if (value != null) {
process(value.trim());
}
Réservez le catch aux choses qui échappent réellement à votre contrôle : saisie utilisateur invalide, fichiers manquants, pannes réseau.
Ensuite : NullPointerException
L'exception la plus courante que vous capturerez (et provoquerez) en Java est la NullPointerException : elle apparaît à l'instant où vous appelez une méthode sur quelque chose qui s'avère être null. Nous allons ensuite examiner précisément ce qui la déclenche, comment lire sa stack trace et les habitudes qui l'empêchent de se produire dès le départ.
Questions fréquentes
Comment utiliser try-catch en Java ?
Placez le code susceptible de lancer une exception dans un bloc try, puis ajoutez un bloc catch indiquant le type d'exception que vous voulez gérer : try { risky(); } catch (IOException e) { ... }. Si le code du try lance une exception correspondante, Java saute directement au bloc catch au lieu de planter. La variable (e) contient l'objet exception, ce qui vous permet de lire son message avec e.getMessage().
À quoi sert le bloc finally en Java ?
Un bloc finally s'exécute après try/catch quoi qu'il arrive : que le code ait réussi, lancé une exception ou même fait un return anticipé. C'est l'endroit pour le nettoyage qui doit toujours avoir lieu, comme fermer un fichier ou libérer un verrou. Pour fermer des ressources, le try-with-resources est généralement plus propre car il les ferme automatiquement.
Faut-il capturer Exception ou un type d'exception précis ?
Capturez le type le plus précis que vous savez réellement gérer. Capturer Exception (ou pire, Throwable) avale tous les problèmes - y compris des bugs comme NullPointerException - et masque la véritable cause. Capturez NumberFormatException si c'est ce que vous attendez, et laissez les exceptions inattendues se propager pour en être informé.