⚠️ Le cours : gérer les erreurs
Quand quelque chose se passe mal (fichier introuvable, division par zéro, texte non numérique…), Java lève une exception. Si personne ne l'attrape, le programme s'arrête et affiche la trace de la pile (*stack trace*). Gérer les exceptions, c'est décider quoi faire à la place : redemander la saisie, utiliser une valeur par défaut, prévenir l'utilisateur.
Lire un message d'erreur
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Division.main(Division.java:5)
- Le type (
ArithmeticException) dit la nature du problème.
- Le message (
/ by zero) précise la cause.
- La ligne (
Division.java:5) dit où ça s'est produit : va-y d'abord.
try, catch et le programme qui continue
Le code à risque est placé dans try. Dès qu'une exception est levée, le reste du bloc est abandonné et l'exécution reprend dans le catch correspondant, puis après le bloc.
public class Division {
static int diviser(int a, int b) {
return a / b;
}
public static void main(String[] args) {
try {
System.out.println(diviser(10, 2));
System.out.println(diviser(1, 0));
System.out.println("jamais affiche");
} catch (ArithmeticException e) {
System.out.println("division par zero : " + e.getMessage());
}
System.out.println("le programme continue");
}
}
Affichage5
division par zero : / by zero
le programme continue
La famille des exceptions
Throwable
├── Error problèmes graves de la JVM (OutOfMemoryError…) : on ne les attrape pas
└── Exception
├── RuntimeException NON vérifiées : bugs de programmation
│ (NullPointerException, ArrayIndexOutOfBoundsException,
│ ArithmeticException, IllegalArgumentException, NumberFormatException)
└── IOException, … VÉRIFIÉES : à déclarer (throws) ou à attraper
| Exception | Cause typique |
NumberFormatException | Integer.parseInt("abc") |
ArithmeticException | division entière par zéro |
ArrayIndexOutOfBoundsException | indice hors du tableau |
NullPointerException | appel d'une méthode sur une référence null |
InputMismatchException | nextInt() alors que l'utilisateur tape du texte |
FileNotFoundException | fichier introuvable (vérifiée) |
Plusieurs catch et finally
On peut enchaîner plusieurs catch pour réagir différemment selon le type. Le premier qui correspond est choisi : place donc les types les plus précis en premier. Le bloc finally s'exécute dans tous les cas, même après un return : c'est l'endroit pour fermer un fichier ou une connexion. Ici, une valeur invalide est ignorée sans arrêter le calcul.
public class Plusieurs {
public static void main(String[] args) {
String[] valeurs = {"12", "x", "7"};
int somme = 0;
for (String texte : valeurs) {
try {
somme += Integer.parseInt(texte);
} catch (NumberFormatException e) {
System.out.println("ignore : " + texte);
}
}
System.out.println("somme = " + somme);
}
}
Affichageignore : x
somme = 19
Pour traiter plusieurs types de la même façon : catch (NumberFormatException | ArrayIndexOutOfBoundsException e) { … }.
Lever une exception : throw
Une méthode qui reçoit un argument inacceptable ne doit pas continuer avec une valeur absurde : elle lève une exception, avec un message qui explique le problème (voir l'exemple de la deuxième carte). L'appelant décide ensuite de l'attraper ou non.
Exceptions vérifiées et personnalisées
Une exception vérifiée (qui hérite de Exception sans être une RuntimeException) oblige le compilateur à contrôler que tu la traites : soit par un catch, soit en la déclarant avec throws dans la signature de la méthode. On crée ses propres exceptions pour exprimer des erreurs du domaine (ici, un solde insuffisant).
class SoldeInsuffisant extends Exception {
SoldeInsuffisant(String message) {
super(message);
}
}
class Compte {
private double solde;
Compte(double solde) {
this.solde = solde;
}
void retirer(double montant) throws SoldeInsuffisant {
if (montant > solde) {
throw new SoldeInsuffisant("manque " + (montant - solde) + " euros");
}
solde -= montant;
}
}
public class RetraitBanque {
public static void main(String[] args) {
Compte c = new Compte(50);
try {
c.retirer(20);
c.retirer(100);
} catch (SoldeInsuffisant e) {
System.out.println("refuse : " + e.getMessage());
}
}
}
Affichagerefuse : manque 70.0 euros
try-with-resources : fermer automatiquement
Une ressource (fichier, Scanner, connexion) doit être fermée même en cas d'erreur. Déclarée entre les parenthèses du try, elle est fermée automatiquement à la sortie du bloc, sans finally.
import java.util.Scanner;
public class Ressource {
public static void main(String[] args) {
try (Scanner sc = new Scanner("12 7 5")) {
int somme = 0;
while (sc.hasNextInt()) {
somme += sc.nextInt();
}
System.out.println("somme = " + somme);
} // sc est fermé automatiquement
}
}
Bonnes pratiques
- Attrape le type d'exception le plus précis possible, pas systématiquement
Exception.
- N'écris jamais un
catch vide : au minimum, affiche le message ou journalise-le.
- Préfère prévenir l'erreur quand c'est simple (
if (b != 0), vérifier une saisie) ; réserve les exceptions aux situations vraiment exceptionnelles.
- Donne un message utile quand tu lèves une exception : la valeur fautive, ce qui était attendu.