Comment reconnaître les différentes classes d’exception en Java?

Vous devez reconnaître trois groupes de classes d’exception : RuntimeException, Exception vérifiée (checked Exception), et Error. Nous allons examiner des exemples courants de chaque type. Vous devrez reconnaître quel type d’exception c’est et si elle est lancée par la Machine Virtuelle Java (JVM) ou par un programmeur. Pour certaines exceptions, vous devez également savoir lesquelles sont héritées les unes des autres.

Classes RuntimeException

RuntimeException et ses sous-classes sont des exceptions non vérifiées qui n’ont pas besoin d’être gérées ou déclarées. Elles peuvent être lancées par le programmeur ou la JVM. Les classes d’exceptions non vérifiées courantes sont répertoriées dans le Tableau 11.2.

Exception non vérifiéeDescription
ArithmeticExceptionLancée lorsque le code tente de diviser par zéro.
ArrayIndexOutOfBoundsExceptionLancée lorsque le code utilise un index illégal pour accéder à un tableau.
ClassCastExceptionLancée lorsqu’une tentative est faite de convertir un objet en une classe dont il n’est pas une instance.
NullPointerExceptionLancée lorsqu’il y a une référence null là où un objet est requis.
IllegalArgumentExceptionLancée par le programmeur pour indiquer qu’une méthode a reçu un argument illégal ou inapproprié.
NumberFormatExceptionSous-classe de IllegalArgumentException. Lancée lorsqu’une tentative est faite de convertir une chaîne en type numérique mais la chaîne n’a pas le format approprié.

ArithmeticException

Essayer de diviser un int par zéro donne un résultat indéfini. Lorsque cela se produit, la JVM lance une ArithmeticException :

int reponse = 11 / 0;

L’exécution de ce code donne le résultat suivant :

Exception in thread "main" java.lang.ArithmeticException: / by zero

Java n’épelle pas le mot diviser. C’est acceptable, cependant, parce que nous savons que / est l’opérateur de division et que Java essaie de vous dire qu’une division par zéro s’est produite.

Le thread “main” vous indique que le code a été appelé directement ou indirectement à partir d’un programme avec une méthode main. Ensuite vient le nom de l’exception, suivi d’informations supplémentaires (le cas échéant) qui accompagnent l’exception.

ArrayIndexOutOfBoundsException

Vous savez maintenant que les index de tableau commencent à 0 et vont jusqu’à 1 moins que la longueur du tableau — ce qui signifie que ce code lancera une ArrayIndexOutOfBoundsException :

int[] dénombrementDeÉlans = new int[3];
System.out.println(dénombrementDeÉlans[-1]);

C’est un problème car il n’existe pas d’index de tableau négatif. L’exécution de ce code donne le résultat suivant :

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException:
Index -1 out of bounds for length 3

ClassCastException

Java essaie de vous protéger des conversions impossibles. Ce code ne compile pas car Integer n’est pas une sous-classe de String :

String type = "élan";
Integer nombre = (Integer) type; // NE COMPILE PAS

Un code plus compliqué déjoue les tentatives de Java pour vous protéger. Lorsque la conversion échoue au moment de l’exécution, Java lancera une ClassCastException :

String type = "élan";
Object obj = type;
Integer nombre = (Integer) obj; // ClassCastException

Le compilateur voit une conversion de Object à Integer. Cela pourrait être correct. Le compilateur ne réalise pas qu’il y a un String dans cet Object. Lorsque le code s’exécute, il produit la sortie suivante :

Exception in thread "main" java.lang.ClassCastException:
java.base/java.lang.String
cannot be cast to java.lang.base/java.lang.Integer

Java vous indique les deux types impliqués dans le problème, rendant évident ce qui ne va pas.

NullPointerException

Les variables d’instance et les méthodes doivent être appelées sur une référence non-null. Si la référence est null, la JVM lancera une NullPointerException.

public class Grenouille {
    public void sauter(String nom, Integer saut) {
        System.out.print(nom.toLowerCase() + " " + saut.intValue());
    }

    public static void main(String[] args) {
        new Grenouille().sauter(null, 1);
    }
}

L’exécution de ce code donne le résultat suivant :

Exception in thread "main" java.lang.NullPointerException: Cannot invoke 
"String.toLowerCase()" because "parameter1" is null

Si vous êtes nouveau dans Java 17, vous auriez dû remarquer quelque chose de spécial à propos de la sortie. La JVM vous indique maintenant la référence d’objet qui a déclenché la NullPointerException ! Cette nouvelle fonctionnalité est appelée Helpful NullPointerExceptions (NullPointerExceptions utiles).

Comme autre exemple, supposons que nous changions la ligne 7 :

new Grenouille().sauter("Kermit", null);

Alors la sortie à l’exécution change comme suit :

Exception in thread "main" java.lang.NullPointerException: Cannot invoke
"java.lang.Integer.intValue()" because "parameter2" is null

Par défaut, une NullPointerException sur une variable locale ou un paramètre de méthode est imprimée avec un numéro indiquant l’ordre dans lequel elle apparaît dans la méthode, tel que <local2> ou <parameter4>. Si vous êtes comme nous et souhaitez que le nom réel de la variable soit affiché, compilez le code avec l’option -g:vars, qui ajoute des informations de débogage. Dans les exemples précédents, <parameter1> et <parameter2> sont alors remplacés par nom et saut, respectivement.

Activer/Désactiver les NullPointerExceptions utiles

Lorsque les NullPointerExceptions utiles ont été ajoutées dans Java 14, la fonctionnalité était désactivée par défaut et devait être activée via un argument de ligne de commande ShowCodeDetailsInExceptionMessages à la JVM :

java -XX:+ShowCodeDetailsInExceptionMessages Grenouille

Dans Java 15 et au-dessus, le comportement par défaut a été modifié pour qu’il soit activé par défaut, bien qu’il puisse toujours être désactivé via l’argument de ligne de commande.

java -XX:-ShowCodeDetailsInExceptionMessages Grenouille

IllegalArgumentException

IllegalArgumentException est une façon pour votre programme de se protéger. Vous voulez dire à l’appelant que quelque chose ne va pas—de préférence d’une manière évidente que l’appelant ne peut pas ignorer afin que le programmeur corrige le problème. Voir le code se terminer par une exception est un excellent rappel que quelque chose ne va pas. Considérez cet exemple lorsqu’il est appelé comme setNombreOeufs(-2) :

public void setNombreOeufs(int nombreOeufs) {
    if (nombreOeufs < 0)
        throw new IllegalArgumentException("# oeufs ne doit pas être négatif");
    this.nombreOeufs = nombreOeufs;
}

Le programme lance une exception lorsqu’il n’est pas satisfait des valeurs des paramètres. La sortie ressemble à ceci :

Exception in thread "main"
java.lang.IllegalArgumentException: # oeufs ne doit pas être négatif

Clairement, c’est un problème qui doit être résolu si le programmeur veut que le programme fasse quelque chose d’utile.

NumberFormatException

Java fournit des méthodes pour convertir des chaînes en nombres. Lorsque celles-ci reçoivent une valeur invalide, elles lancent une NumberFormatException. L’idée est similaire à IllegalArgumentException. Puisque c’est un problème courant, Java lui donne une classe séparée. En fait, NumberFormatException est une sous-classe de IllegalArgumentException. Voici un exemple d’essai de conversion de quelque chose de non numérique en int :

Integer.parseInt("abc");

La sortie ressemble à ceci :

Exception in thread "main"
java.lang.NumberFormatException: For input string: "abc"

Pour l’examen, vous devez savoir que NumberFormatException est une sous-classe de IllegalArgumentException. Nous couvrons plus à propos de pourquoi c’est important plus tard dans le chapitre.

Classes Exception vérifiées

Les exceptions vérifiées ont Exception dans leur hiérarchie mais pas RuntimeException. Elles doivent être gérées ou déclarées. Les exceptions vérifiées courantes sont répertoriées dans le Tableau 11.3.

Exception vérifiéeDescription
FileNotFoundExceptionSous-classe de IOException. Lancée programmatiquement lorsque le code essaie de référencer un fichier qui n’existe pas.
IOExceptionLancée programmatiquement lorsqu’il y a un problème de lecture ou d’écriture de fichier.
NotSerializableExceptionSous-classe de IOException. Lancée programmatiquement lors de tentative de sérialiser ou désérialiser une classe non sérialisable.
ParseExceptionIndique un problème d’analyse d’entrée.
SQLExceptionLancée lorsqu’une erreur est liée à l’accès à une base de données.

Vous devez savoir que ce sont toutes des exceptions vérifiées qui doivent être gérées ou déclarées. Vous devez également savoir que FileNotFoundException et NotSerializableException sont des sous-classes de IOException. Vous verrez ces trois classes dans le Chapitre 14, “I/O,” et SQLException dans le Chapitre 15, “JDBC.”

Classes Error

Les erreurs sont des exceptions non vérifiées qui étendent la classe Error. Elles sont lancées par la JVM et ne devraient pas être gérées ou déclarées. Les erreurs sont rares, mais vous pourriez voir celles listées dans le Tableau 11.4.

ErrorDescription
ExceptionInInitializerErrorLancée lorsqu’un initialiseur statique lance une exception et ne la gère pas
StackOverflowErrorLancée lorsqu’une méthode s’appelle elle-même trop de fois (appelée récursion infinie car la méthode appelle typiquement elle-même sans fin)
NoClassDefFoundErrorLancée lorsqu’une classe que le code utilise est disponible au moment de la compilation mais pas à l’exécution

Vous devez juste savoir que ces erreurs sont non vérifiées et que le code est souvent incapable de s’en remettre.