12 - Gestion des Exceptions
Apprenez à gérer les erreurs avec grâce dans vos programmes Java.
Les exceptions sont des erreurs qui arrivent pendant que votre programme est exécuté. Au lieu de crasher, vous pouvez "attraper" (catch) ces erreurs et les gérer avec grâce.
Qu'est-ce qu'une Exception ?
Une exception est un événement qui perturbe le flux normal de votre programme :
String text = null;
System.out.println(text.length()); // NullPointerException - crash !Sans gestion, cela ferait crasher votre mod et casserait le jeu !
Blocs d'instructions Try-Catch
Utilisez try-catch pour gérer les exceptions :
try {
// Code qui pourrait lancer une exception
String text = null;
System.out.println(text.length());
} catch (NullPointerException e) {
// Code pour gérer l'erreur
System.out.println("Erreur : la variable text était null !");
}
System.out.println("Le programme continue...");- Le code dans le bloc
trys'exécute normalement - Si une exception arrive, l'exécution passe dans le bloc
catch - Après le bloc
catch, le programme continue normalement - Si aucune exception n'arrive, le bloc
catchest ignoré
try {
// Essaie de faire quelque chose de risqué
int result = 10 / 0; // Division par zéro !
} catch (ArithmeticException e) {
// Gère l'erreur
System.out.println("On ne peut pas diviser par zéro !");
}
// Le programme continue de s'exécuterLes types courants d'Exception
NullPointerException
Tentative d'accéder aux méthodes/propriétés sur des objets null :
String name = null;
try {
int length = name.length();
} catch (NullPointerException e) {
System.out.println("name est null !");
}ArrayIndexOutOfBoundsException
Tentative d'accéder à un indice de tableau invalide :
int[] numbers = { 1, 2, 3 };
try {
int value = numbers[10]; // L'indice 10 n'existe pas !
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Indice de tableau invalide !");
}NumberFormatException
Tentative de convertir un String invalide en nombre :
try {
int number = Integer.parseInt("abc"); // Pas un nombre !
} catch (NumberFormatException e) {
System.out.println("Format de nombre invalide !");
}ArithmeticException
Les erreurs mathématiques comme la division par zéro :
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("Erreur mathématique !");
}Plusieurs blocs Catch
Gérer différentes exceptions différemment :
String input = "abc";
try {
int number = Integer.parseInt(input);
int result = 100 / number;
System.out.println(result);
} catch (NumberFormatException e) {
System.out.println("Nombre invalide !");
} catch (ArithmeticException e) {
System.out.println("Impossible de diviser par zéro !");
}Attraper plusieurs types d'exception
Attrapez plusieurs exceptions dans un seul bloc :
try {
// Code risqué
} catch (NumberFormatException | ArithmeticException e) {
System.out.println("Une erreur relative aux mathématiques est arrivée !");
}Le bloc Finally
Du code qui s'exécute toujours, qu'une exception arrive ou non :
try {
System.out.println("Ouverture du fichier...");
// Code qui peut échouer
} catch (Exception e) {
System.out.println("Erreur : " + e.getMessage());
} finally {
System.out.println("Fermeture du fichier...");
// Ceci s'exécute TOUJOURS - pratique pour le nettoyage
}Utilisez finally pour des tâches de nettoyage qui doivent toujours se faire :
- Fermer des fichiers
- Libérer des ressources
- Sauvegarder des données
- Remplir le Log
FileReader file = null;
try {
file = new FileReader("data.txt");
// Lis le fichier
} catch (Exception e) {
System.out.println("Erreur en lisant le fichier");
} finally {
if (file != null) {
file.close(); // Ferme toujours le fichier !
}
}Obtenir des informations des Exceptions
L'objet exception contient des informations utiles
try {
int result = Integer.parseInt("xyz");
} catch (NumberFormatException e) {
System.out.println("Message : " + e.getMessage());
System.out.println("Type : " + e.getClass().getName());
e.printStackTrace(); // Afficher tous les détails de l'erreur
}Exemples pratiques
Input du joueur sécurisé
import java.util.Scanner;
public class PlayerInput {
public static int getPlayerChoice(Scanner scanner) {
while (true) {
try {
System.out.print("Entrez votre choix (1-5): ");
String input = scanner.nextLine();
int choice = Integer.parseInt(input);
if (choice < 1 || choice > 5) {
System.out.println("Entrez un chiffre entre 1 et 5 s'il vous plaît");
continue;
}
return choice;
} catch (NumberFormatException e) {
System.out.println("Entrée invalide, entrez un chiffre s'il vous plaît.");
}
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int choice = getPlayerChoice(scanner);
System.out.println("Vous avez choisi : " + choice);
}
}Mise à jour de la durabilité d'un objet sécurisée
public class Item {
private String name;
private int durability;
private int maxDurability;
public Item(String name, int maxDurability) {
this.name = name;
this.durability = maxDurability;
this.maxDurability = maxDurability;
}
public void damage(int amount) {
try {
if (amount < 0) {
throw new IllegalArgumentException("Les dégâts ne peuvent pas être négatifs !");
}
durability -= amount;
if (durability < 0) {
durability = 0;
}
System.out.println(name + " durabilité : " + durability + "/" + maxDurability);
if (durability == 0) {
System.out.println(name + " s'est cassé !");
}
} catch (IllegalArgumentException e) {
System.out.println("Erreur : " + e.getMessage());
}
}
}Chargement de configuration sécurisé
import java.util.HashMap;
public class ConfigLoader {
private HashMap<String, String> settings;
public ConfigLoader() {
this.settings = new HashMap<>();
loadDefaults();
}
private void loadDefaults() {
settings.put("joueursMax", "10");
settings.put("difficulté", "normale");
settings.put("pvpActivé", "true");
}
public int getIntSetting(String key, int defaultValue) {
try {
String value = settings.get(key);
if (value == null) {
return defaultValue;
}
return Integer.parseInt(value);
} catch (NumberFormatException e) {
System.out.println("Nombre invalide pour " + key + ", utilisation de la valeur par défaut");
return defaultValue;
}
}
public boolean getBooleanSetting(String key, boolean defaultValue) {
try {
String value = settings.get(key);
if (value == null) {
return defaultValue;
}
return Boolean.parseBoolean(value);
} catch (Exception e) {
System.out.println("Booléen invalide pour " + key + ", utilisation de la valeur par défaut");
return defaultValue;
}
}
public void setSetting(String key, String value) {
if (key == null || value == null) {
System.out.println("Clé et valeur ne peuvent pas être null !");
return;
}
settings.put(key, value);
}
}Accès à un tableau sécurisés
public class Inventory {
private String[] items;
public Inventory(int size) {
this.items = new String[size];
}
public boolean setItem(int slot, String item) {
try {
if (slot < 0 || slot >= items.length) {
throw new ArrayIndexOutOfBoundsException("Emplacement invalide : " + slot);
}
items[slot] = item;
System.out.println(item + " placé dans l'emplacement " + slot);
return true;
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Erreur : " + e.getMessage());
return false;
}
}
public String getItem(int slot) {
try {
return items[slot];
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Emplacement invalide : " + slot);
return null;
}
}
}Parseur de commande avec gestion d'erreur
public class CommandParser {
public static void parseCommand(String command) {
try {
if (command == null || command.trim().isEmpty()) {
throw new IllegalArgumentException("Une commande ne peut pas être vide !");
}
String[] parts = command.split(" ");
if (parts.length < 2) {
throw new IllegalArgumentException("Format de commande invalide !");
}
String action = parts[0];
String target = parts[1];
switch (action) {
case "give":
if (parts.length < 4) {
throw new IllegalArgumentException("Usage : give <player> <item> <amount>");
}
String item = parts[2];
int amount = Integer.parseInt(parts[3]);
if (amount <= 0) {
throw new IllegalArgumentException("La quantité doit être positive !");
}
System.out.println("Don de " + amount + " " + item + " à " + target);
break;
case "tp":
if (parts.length < 5) {
throw new IllegalArgumentException("Usage : tp <player> <x> <y> <z>");
}
int x = Integer.parseInt(parts[2]);
int y = Integer.parseInt(parts[3]);
int z = Integer.parseInt(parts[4]);
System.out.println("Téléporte " + target + " en " + x + ", " + y + ", " + z);
break;
default:
throw new IllegalArgumentException("Commande inconnue : " + action);
}
} catch (NumberFormatException e) {
System.out.println("Erreur : nombre invalide dans la commande !");
} catch (IllegalArgumentException e) {
System.out.println("Erreur : " + e.getMessage());
} catch (Exception e) {
System.out.println("Erreur inattendue : " + e.getMessage());
}
}
public static void main(String[] args) {
parseCommand("give Steve diamond 5");
parseCommand("give Steve sword abc"); // Nombre invalide
parseCommand("tp Alice 10 64 20");
parseCommand("unknown command"); // Commande inconnue
}
}Lancer des Exceptions
Vous pouvez lancer vos propres exceptions :
public class Player {
private int health;
public void setHealth(int health) {
if (health < 0) {
throw new IllegalArgumentException("La vie ne peut pas être négative !");
}
if (health > 100) {
throw new IllegalArgumentException("La vie ne peut pas excéder 100 !");
}
this.health = health;
}
}Lancez des exceptions quand :
- Une méthode reçoit une entrée invalide
- Une opération ne peut pas être réalisée
- Des préconditions ne sont pas réunies
- Quelque chose d'inattendu arrive
public void damagePlayer(int damage) {
if (damage < 0) {
throw new IllegalArgumentException("Les dégâts doivent être positifs !");
}
// Appliquer les dégâts
}
public Item getItem(int slot) {
if (slot < 0 || slot >= inventory.length) {
throw new IndexOutOfBoundsException("Emplacement d'inventaire invalide !");
}
return inventory[slot];
}Les Exceptions vérifiées vs non vérifiées
Exceptions non vérifiées (RuntimeException) :
- N'ont pas besoin d'être attrapées
- Généralement des erreurs de programmation
- Exemples : NullPointerException, ArrayIndexOutOfBoundsException
Exceptions vérifiées :
- Doivent être attrapées ou déclarées
- Généralement les erreurs externes (fichiers, réseau)
- Exemples : IOException, FileNotFoundException
// Non vérifiée - pas besoin d'attraper
int result = 10 / 0; // ArithmeticException
// Vérifiée - doit attraper ou déclarer
try {
FileReader file = new FileReader("data.txt"); // IOException
} catch (IOException e) {
// Gérer l'erreur
}Exercices pratiques
-
Calculateur de division sécurisé : créez une méthode qui divise deux nombres. Gérez la division par zéro et les entrées invalides avec grâce.
-
Setter de niveau de joueur : créez une méthode qui définit le niveau du joueur (1-100). Lancez une exception si invalide, et gérez-le dans le main.
-
Accès à un tableau sécurisés : créez une méthode qui accède aux éléments d'un tableau de manière sécurisée. Renvoie null si l'index est invalide au lieu de crasher.
-
Parseur de configuration : lisez un String de configuration comme "joueursMax=10" et parsez-le. Gérez les formats invalides avec grâce.
-
Vérificateur de nom de fichier : créez une méthode qui vérifie si un nom de fichier est valide (pas de caractères spéciaux, pas vide). Lancez des exceptions pour les noms invalides.