Gestion d'exceptions en Java
Une exception est un événement inattendu survenant pendant l'exécution du programme. Cela affecte le flux des instructions du programme, ce qui peut entraîner une interruption anormale du programme.
Une exception peut se produire pour plusieurs raisons. Certains d'entre eux sont:
- Entrée utilisateur non valide
- Échec d’un périphérique
- Perte de connexion réseau
- Limites physiques (mémoire insuffisante)
- Erreurs de code
- Ouvrir un fichier indisponible
Ces erreurs sont appelées exceptions car elles ne sont vraisemblablement pas des occurrences habituelles. elles sont «exceptionnelles». La gestion des exceptions est le nom des techniques orientées objet qui gèrent ou résolvent de telles erreurs. Les exceptions non planifiées qui se produisent lors de l’exécution d’un programme sont également appelées exceptions d’exécution, par opposition aux erreurs de syntaxe découvertes lors de la compilation du programme.
- La classe Error représente des erreurs plus graves que votre programme ne peut généralement pas récupérer. Par exemple, la mémoire peut être insuffisante pour exécuter un programme. Généralement, vous n'utilisez ni n'implémentez des objets Error dans vos programmes. Un programme ne peut pas récupérer seul des conditions d'erreur.
- La classe Exception comprend les erreurs moins graves qui représentent des conditions inhabituelles survenant pendant l'exécution d'un programme et à partir desquelles le programme peut être restauré. Par exemple, un type d'erreur de classe Exception se produit si un programme utilise une valeur d'indice de tableau non valide. Le programme peut être restauré en affectant une valeur valide à la variable d'indice.
la figure suivante montre la hiérarchie des exceptions
Comme vous pouvez le voir sur la figure ci-dessus, la classe Throwable est la classe racine de la hiérarchie.
La liste des messages d'erreur après chaque tentative d'exécution est appelée trace de pile. La liste montre chaque méthode appelée lors de l'exécution du programme.
Les programmes capables de gérer les exceptions de manière appropriée sont plus tolérants aux pannes et robustes. Les applications à tolérance de pannes sont conçues pour continuer à fonctionner, éventuellement à un niveau réduit, en cas de défaillance d'une partie du système. La robustesse représente la capacité d'un système à résister aux contraintes et à continuer à fonctionner.
Même si vous choisissez de ne jamais utiliser de techniques de gestion des exceptions orientées objet dans vos propres programmes, vous devez les comprendre, car les méthodes Java intégrées génèrent des exceptions.
Le tableau suivant présente quelques méthodes d’exceptions utiles.
Méthode | Description |
---|---|
public String getMessage() | Renvoie un message détaillé sur l'exception qui s'est produite. Ce message est initialisé dans le constructeur Throwable. |
public Throwable getCause() | Renvoie la cause de l'exception représentée par un objet Throwable. |
public String toString() | Renvoie le nom de la classe concaténée avec le résultat de getMessage (). |
public void printStackTrace() | affiche le résultat de toString() avec la trace de pile dans System.err, le flux de sortie d'erreur. |
public StackTraceElement [] getStackTrace() | Retourne un tableau contenant chaque élément sur la trace de la pile. L'élément à l'index 0 représente le haut de la pile d'appels et le dernier élément du tableau représente la méthode au bas de la pile d'appels. |
public Throwable fillInStackTrace() | Remplit la trace de pile de cet objet Throwable avec la trace de pile actuelle, en ajoutant à toute information précédente de la trace de pile. |
Capture et traitement des exceptions
Dans la terminologie orientée objet, vous essayez (try) une procédure pouvant provoquer une erreur. Une méthode qui détecte une condition d'erreur lève une exception (throws) et si vous écrivez un bloc de code qui traite l'erreur, ce bloc est dit intercepte l'exception (catch).
Lorsque vous créez un segment de code dans lequel une erreur peut survenir, vous placez le code dans un bloc try, qui est un bloc de code que vous essayez d'exécuter tout en reconnaissant qu'une exception pourrait se produire. Un bloc try comprend les éléments suivants:
- Le mot clé try suivi d'une paire d'accolades
- Des instructions exécutables entre les accolades, y compris certaines instructions pouvant provoquer des exceptions
try{ // liste des instructions }
Pour gérer une exception levée, vous pouvez coder un ou plusieurs blocs catch immédiatement après un bloc try. Un bloc catch est un segment de code qui peut gérer une exception peut être levée par le bloc try qui le précède.
L'exception pourrait être celle qui est lancée automatiquement, ou vous pourriez écrire explicitement une instruction throw. Une instruction throw est une instruction qui envoie un objet Exception à partir d'un bloc ou d'une méthode afin qu'il puisse être géré ailleurs. Une exception levée peut être attrapée par un bloc catch. Chaque bloc catch peut “attraper” un type d'exception, à savoir un objet qui est un objet de type Exception ou l'une de ses classes enfants. Vous créez un bloc catch en tapant les éléments suivants:
- Le mot clé catch suivi d'une paire de parenthèses
- Entre les parenthèses, un type d’exception et un identifiant pour une instance.
- Une paire d'accolades contenant des instructions qui exécutent les actions que vous souhaitez utiliser pour gérer la condition d'erreur.
catch(TypeException var){ // gérer la condition d'erreur }
Syntaxe :
try{ // traitements } catch(TypeException var){ // gérer la condition d'erreur }
Exemple 1 :
import java.util.Scanner; import java.util.InputMismatchException; public class Test { public static void main(String args[]) { int a, b; Scanner clavier = new Scanner(System.in); try { System.out.print("Saisir a : "); a = clavier.nextInt(); System.out.print("Saisir b : "); b = clavier.nextInt(); System.out.println("a+b = " + (a + b)); } catch (InputMismatchException e) { System.out.println(e); } // fermer les ressources clavier.close(); } }
b étant un entier, lorsqu’une valeur illégale est tentée, une exception InputMismatchException est créée automatiquement et le bloc catch est exécuté.
Vous pouvez inclure toutes les instructions Java légales dans un bloc try ou un bloc catch, y compris les déclarations de variable. Cependant, vous devez vous rappeler qu'une variable déclarée dans un bloc est locale à ce bloc. En d'autres termes, la variable sort de la portée lorsque le bloc try ou catch se termine, ainsi toute variable déclarée dans l'un des blocs ne doit servir qu'un objectif temporaire.
Si vous souhaitez utiliser une variable à la fois avec un bloc try ou catch et après, vous devez alors déclarer la variable avant le début du bloc try. Cependant, si vous déclarez une variable avant un bloc try mais attendez d'affecter sa valeur utilisable initiale dans le bloc try ... catch, vous devez veiller à ce que la variable reçoive une valeur utile. Sinon, lorsque vous utilisez la variable après la fin de la paire try ... catch, le programme ne sera pas compilé.
Multiples blocs catch
Vous pouvez placer autant d'instructions que nécessaire dans un bloc try et intercepter autant d'exceptions que vous le souhaitez. Si vous essayez plusieurs instructions, seule la première instruction générant une erreur lève une exception. Dès que l'exception se produite, la logique est transférée vers le bloc catch, ce qui laisse le reste des instructions du bloc try non exécutées.
Lorsqu'un programme contient plusieurs blocs catch, ceux-ci sont examinés dans l'ordre jusqu'à ce qu'une correspondance soit trouvée pour le type d'exception qui s'est produite. Ensuite, le bloc catch correspondant est exécuté et chaque bloc catch restant est ignoré.
Lorsque vous répertoriez plusieurs blocs catch après un bloc try, vous devez veiller à ce que certains blocs catch ne deviennent pas inaccessibles. Les instructions inaccessibles sont des instructions de programme qui ne peuvent jamais être exécutées en aucune circonstance. Par exemple, si deux blocs de capture successifs interceptent respectivement une exception ArithmeticException et une exception ordinaire, les erreurs ArithmeticException entraînent l'exécution de la première capture et les autres types dérivant d'Exception « tombent » dans le bloc de catch Exception plus général. Cependant, si vous inversez la séquence des blocs catch de sorte que celui qui capture les objets Exception généraux soit premier, même ArithmeticExceptions serait capturé par le contrôle Exception. Le bloc de capture ArithmeticException est donc inaccessible car le bloc de catch Exception se trouve sur son chemin et la classe ne se compile pas. Pensez à organiser vos blocs d'arrêt afin que le "panier plus grand" soit toujours inférieur à "un panier plus petit". C'est-à-dire que chaque exception devrait « échouer » autant de blocs de capture que nécessaire pour atteindre celui qui la tiendra.
Parfois, vous voulez exécuter le même code peu importe le type d'exception qui se produite. Dans ce cas, vous n'attrapez que Exception
Exemple 1 :
try{ // traitements } catch(Exception e){ System.out.println(e); }
Le bloc catch de l'exemple ci-dessus accepte un type d'argument Exception plus générique que celui renvoyé par l'une des instructions try potentiellement génératrices d'erreurs. Le bloc catch générique peut donc servir de bloc "attrape-tout".
Un bloc catch peut également être écrit pour intercepter plusieurs types d'exceptions spécifiques. Par exemple, le bloc catch suivant capture deux types d'exception. Quand l'un ou l'autre est attrapé, son identifiant local est e
try{ // traitements } catch(ArithmeticException, InputMismatchException e) { System.out.println(e); }
Utiliser le bloc finally
Lorsque vous avez des actions à effectuer à la fin d’une séquence try...catch, vous pouvez utilisez un bloc finally. Le code dans un bloc finally s'exécute que le bloc try précédent identifie une exception ou non. En règle générale, vous utilisez un bloc finally pour effectuer des tâches de nettoyage qui doivent être effectuées, que des exceptions se soient produites ou non.
Syntaxe :
try { // instructions } catch(Exception e) { // actions si une exception a été lancée } finally { // nettoyage }
Cependant, le dernier ensemble d'instructions(finally) peut ne jamais s'exécuter pour au moins deux raisons:
- Tout bloc try peut renvoyer un objet Exception pour lequel vous n'avez pas fourni de bloc catch.
- Le bloc try ou catch peut contenir une instruction System.exit (); qui arrête immédiatement l'exécution.
Lorsque vous incluez un bloc finally, vous êtes assuré que les instructions finally seront exécutées avant que la méthode ne soit abandonnée. Par exemple, les programmeurs utilisent souvent un bloc finally lorsque le programme utilise des fichiers de données qui doivent être fermés.
Spécifier les exceptions qu'une méthode peut générer
Si une méthode lève une exception qu'elle n'acceptera pas mais qu'une méthode différente interceptera, vous devez créer une clause throws en utilisant le mot clé throws suivi d'un type d'exception dans l'en-tête de la méthode. Cette pratique est appelée spécification d'exception.
Exemple 1 :
public class listPrix { private static final double[] prix = {15.99, 27.88, 34.56, 45.89}; public static void afficherPrix(int elem) throws IndexOutOfBoundsException { System.out.println("le prix est : " + prix[elem]); } }
Pour la plupart des méthodes Java que vous écrivez, vous n'utilisez pas de clause throws. Cependant, dans ces méthodes, si vous divisez par 0 ou si vous dépassez les limites d’un tableau, une exception est néanmoins levée. La plupart du temps, vous laissez Java gérer les exceptions en arrêtant le programme.
Imaginez à quel point vos programmes deviendraient difficiles à gérer si vous deviez fournir des instructions pour gérer toutes les erreurs possibles.
Les seules exceptions qui doivent être interceptées ou nommées dans une clause throws sont le type appelé exceptions vérifiées.
Les exceptions Java peuvent être classées en deux types:
- Exceptions non vérifiées : ces exceptions héritent de la classe Error ou de la classe RuntimeException. Bien que vous puissiez gérer ces exceptions dans votre programmes, vous n'êtes pas obligé de le faire. Par exemple, la division par zéro est un type d'exception RuntimeException et vous n'êtes pas obligé de gérer cette exception. Vous pouvez simplement laisser le programme se terminer.
- Exceptions vérifiées : Ces exceptions sont le type que les programmeurs doivent anticiper et à partir duquel les programmes doivent pouvoir récupérer. Toutes les exceptions que vous lancez explicitement et qui descendent de la classe Exception sont des exceptions vérifiées.
Les programmeurs Java disent que les exceptions vérifiées sont soumises à la capture ou à l'exigence spécifiée, ce qui signifie que si vous générez une exception vérifiée à partir d'une méthode, vous devez effectuer l'une des opérations suivantes:
- catch dans la méthode.
- Spécifiez l’exception dans la clause throws dans l'en-tête de la méthode.
Le code qui utilise une exception vérifiée ne sera pas compilé si la règle catch n'est pas suivie.
Si vous écrivez une méthode avec une clause throws dans l'en-tête, toute méthode qui utilise votre méthode doit effectuer l'une des opérations suivantes:
- Intercepter et gérer l'exception possible.
- Déclarez l'exception dans sa clause throws. La méthode appelée peut alors renvoyer l'exception à une autre méthode qui pourrait l'attraper ou la lancer à nouveau.
En d'autres termes, lorsqu'une exception est une exception vérifiée, les programmeurs sont obligés de gérer la possibilité qu'une exception soit levée.
Si vous écrivez une méthode qui lève explicitement une exception vérifiée qui n'est pas interceptée dans la méthode, Java requiert que vous utilisiez la clause throws dans l'en-tête de la méthode. L'utilisation de la clause throws ne signifie pas que la méthode lève une exception, tout pourrait bien se dérouler sans heurts. Au lieu de cela, cela signifie que la méthode peut générer une exception. Vous incluez la clause throws dans l'en-tête de méthode afin que les applications qui utilisent vos méthodes soient informées du risque d'exception.