Gestion des exceptions
Notions théoriques
Qu'est-ce qu'une exception ?
Une exception est un événement anormal qui se produit pendant l'exécution d'un programme et qui interrompt le flux normal. Par exemple : division par zéro, fichier introuvable, connexion réseau coupée, saisie invalide.
Sans gestion des exceptions, le programme s'arrête brutalement avec un message d'erreur peu compréhensible pour l'utilisateur. La gestion des exceptions permet de réagir proprement à ces situations.
Structure try / catch / finally
try {
// Code qui peut lever une exception
int resultat = 10 / 0;
} catch (ArithmeticException e) {
// Code exécuté si une ArithmeticException est levée
System.out.println("Erreur : " + e.getMessage());
} finally {
// Code TOUJOURS exécuté (avec ou sans exception)
System.out.println("Fin du traitement.");
}
try: le bloc de code "à risque"catch: attrape une exception d'un type précisfinally: s'exécute toujours, même si une exception est levée (utile pour fermer des ressources)
Hiérarchie des exceptions
Throwable
├── Error ← erreurs graves de la JVM (OutOfMemoryError...) - ne pas attraper
└── Exception
├── RuntimeException ← exceptions NON vérifiées (non-checked)
│ ├── NullPointerException
│ ├── ArrayIndexOutOfBoundsException
│ ├── IllegalArgumentException
│ └── NumberFormatException
└── IOException ← exceptions VÉRIFIÉES (checked)
└── FileNotFoundException
Exceptions vérifiées vs non vérifiées
- Exceptions vérifiées (checked) : héritent d'
Exception(mais pas deRuntimeException). Le compilateur oblige à les gérer avectry/catchou à les déclarer avecthrows.- Exemples :
IOException,SQLException
- Exemples :
- Exceptions non vérifiées (unchecked) : héritent de
RuntimeException. Pas obligatoire de les attraper.- Exemples :
NullPointerException,NumberFormatException,IllegalArgumentException
- Exemples :
Déclarer une exception avec throws
Si une méthode peut lever une exception vérifiée, vous pouvez soit la gérer dans la méthode, soit la propager à l'appelant avec throws :
public static String lireFichier(String chemin) throws IOException {
return Files.readString(Path.of(chemin));
// L'appelant devra gérer l'IOException
}
Lever une exception avec throw
Vous pouvez lever une exception vous-même pour signaler une situation invalide :
public static void setAge(int age) {
if (age < 0 || age > 150) {
throw new IllegalArgumentException("L'âge doit être entre 0 et 150, reçu : " + age);
}
this.age = age;
}
Créer sa propre exception
Hérite d'Exception (vérifiée) ou de RuntimeException (non vérifiée) :
// Exception personnalisée vérifiée
public class NoteInvalideException extends Exception {
public NoteInvalideException(String message) {
super(message);
}
}
// Utilisation
public static void setNote(double note) throws NoteInvalideException {
if (note < 0 || note > 20) {
throw new NoteInvalideException("Note invalide : " + note + " (doit être entre 0 et 20)");
}
}
En Java, une classe ne peut hériter que d'une seule classe (extends). En revanche, elle peut implémenter plusieurs interfaces (implements I1, I2, ...). Ce choix évite les conflits d'implémentation qui surviendraient si deux classes parentes définissaient la même méthode différemment.
Attraper plusieurs types d'exceptions
try {
// code à risque
} catch (IOException e) {
System.out.println("Erreur fichier : " + e.getMessage());
} catch (NumberFormatException e) {
System.out.println("Nombre invalide : " + e.getMessage());
} catch (Exception e) {
// Attrape tout le reste (mettre en dernier !)
System.out.println("Erreur inattendue : " + e.getMessage());
}
Mets toujours les types les plus précis avant les types généraux. catch (Exception e) attrape TOUT : si vous le mettez en premier, les blocs catch suivants ne seront jamais atteints.
Exemple pratique
public class GestionExceptions {
// Exception personnalisée
public static class AgeInvalideException extends Exception {
public AgeInvalideException(String message) {
super(message);
}
}
// Méthode qui valide l'âge
public static void validerAge(int age) throws AgeInvalideException {
if (age < 0) {
throw new AgeInvalideException("L'âge ne peut pas être négatif : " + age);
}
if (age > 120) {
throw new AgeInvalideException("L'âge semble irréaliste : " + age);
}
}
// Méthode qui parse et valide
public static int lireAge(String texte) throws AgeInvalideException {
try {
int age = Integer.parseInt(texte);
validerAge(age);
return age;
} catch (NumberFormatException e) {
throw new AgeInvalideException("'" + texte + "' n'est pas un nombre valide");
}
}
public static void main(String[] args) {
String[] tests = {"25", "-5", "abc", "200"};
for (String test : tests) {
try {
int age = lireAge(test);
System.out.println("Âge valide : " + age);
} catch (AgeInvalideException e) {
System.out.println("Erreur : " + e.getMessage());
}
}
}
}
e.getMessage() retourne le message passé au constructeur de l'exception. e.getClass().getSimpleName() retourne le nom de la classe d'exception. Les deux sont utiles pour des messages d'erreur clairs.
Test de mémorisation/compréhension
TP pour réfléchir et résoudre des problèmes
Vous allez créer un système de validation des inscriptions d'élèves avec des exceptions personnalisées.
Étape 1 — Créer l'exception personnalisée
Crée une exception InscriptionException vérifiée avec un message d'erreur.
Par convention, toutes les classes d'exception se terminent par Exception : InscriptionException, NoteInvalideException, ConnexionException. Cela les identifie immédiatement à la lecture du code.
Étape 2 — Méthode de validation avec throw
Crée une méthode qui valide un nom d'élève et lève InscriptionException si le nom est vide.
Le message passé à l'exception doit être suffisamment explicite pour permettre de comprendre et corriger le problème sans chercher dans le code. Inclus la valeur invalide dans le message quand c'est pertinent : "Note invalide : " + note + " (doit être entre 0 et 20)".
Étape 3 — Gérer les exceptions dans main
Dans main, appelle validerNom dans un bloc try/catch et affiche un message d'erreur si la validation échoue.
Un bloc catch vide {} ou qui ne fait que e.printStackTrace() masque les problèmes. Affiche toujours au minimum le message de l'exception, et dans une application réelle, log-le (voir séance suivante sur SLF4J).