2.1. Classe
Throwable
, notion de
stack trace
Les erreurs et exceptions sont en fait des objets Java (tout est objet en Java, donc cette phrase n'apporte pas beaucoup d'information). Ces objets appartiennent à des classes qui étendent toutes, directement ou indirectement, la classe
Throwable
(package
java.lang
).
Cette classe est immédiatement étendue par trois autres classes :
-
Exception
-
RuntimeException
-
Error
Les exceptions qui étendent
Error
et
RuntimeException
sont les
unechecked exception
, pour lesquels il n'est pas besoin d'écrire de code pour les gérer.
En revanche les exceptions qui n'étendent aucune de ces deux classes, qui étendent donc directement
Throwable
ou
Exception
sont les
checked exceptions
qui doivent être gérées dans un bloc
try / catch
ou une clause
throws
.
Un objet de type
Throwable
est construit à partir de deux éléments : un message d'erreur, censé indiquer la nature de l'exception, et une autre exception, censée être la cause première de la génération de cette exception. Ces deux éléments sont facultatifs, on peut aussi générer une exception à partir de rien.
La classe
Throwable
comporte un jeu de méthodes qui permettent de gérer ce que l'on appelle la
stack trace
d'une exception. Cette
stack trace
représente la pile d'appel du programme au moment où l'exception a été générée. Il s'agit d'une liste de noms de classes, assortis d'un numéro de ligne, qui indique très précisément à quel endroit du programme l'erreur a été produite. L'exploitation de cette
stack trace
est primordiale en phase de mise au point des logiciels, et permet le plus souvent de corriger très rapidement les bugs que l'on ne manque pas d'ajouter dans son code.
Voyons dès maintenant un exemple de
stack trace
, relevé lors du démarrage (ou tout du moins de la tentative de démarrage) d'un serveur Tomcat.
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/util/logging/LogFactory
at org.apache.catalina.startup.Bootstrap.<clinit>(Bootstrap.java:54)
Caused by: java.lang.ClassNotFoundException: org.apache.util.logging.LogFactory
at java.net.URLClassLoader$1.run(URLClassLoader.java:217)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
at java.lang.ClassLoader.loadClass(ClassLoader.java:323)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
at java.lang.ClassLoader.loadClass(ClassLoader.java:268)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:336)</clinit>
L'analyse d'une stack trace procède toujours de la même méthode.
La première ligne donne le message d'erreur : ici la machine Java se plaint de ne pas trouver la classe
org/apache/util/logging/LogFactory
. Il s'agit probablement d'un fichier JAR, nécessaire lors de l'exécution de Tomcat, introuvable dans le
classpath
de la machine Java. Cette erreur a été constatée ligne 54 de la classe
org.apache.catalina.startup.Bootstrap
.
Suit la cause de cette erreur : une exception de type
ClassNotFoundException
, rencontrée dans la classe
URLClassLoader
. Cette partie de l'exception n'est pas très intéressante ici, dans la mesure où il s'agit de classes internes à l'API Java. Une exception pour une classe non trouvée intervient toujours dans un class loader, puisque c'est lui qui est chargé de fournir les classes dont nos applications ont besoin. Ce qui nous intéresse c'est l'endroit de notre application qui a demandé cette classe, et cet endroit nous est fourni par la première partie de la
stack trace
. Il arrive toutefois que la situation soit inverse, d'où l'intérêt de posséder les deux parties de la stack trace.
Notons que la cause d'une exception est elle-même une exception, qui peut avoir elle-même sa propre cause. Cette notion de cause est primordiale, et on le voit dans notre exemple : on peut propager en tant que cause une exception rencontrée dans une couche basse d'une application. Cette première exception va probablement en générer d'autres dans d'autres couches, et grâce à ce mécanisme, on peut conserver la
stack trace
de l'exception primaire, et ainsi faciliter le déverminage de l'application.
Ce qu'il est important de comprendre, c'est qu'une stack trace est créée au moment où l'objet
Throwable
est instancié, à partir de la pile d'appel du thread (nous n'avons pas encore vu les threads dans ce cours, disons pour l'instant qu'il s'agit du programme qui s'exécute) dans lequel l'erreur a été constatée. Il est donc très important de préserver cette exception lors des mécanismes de propagation, si l'on ne veut pas perdre cette information précieuse.
Notons enfin que la classe
Throwable
implémente
Serializable
, propriété qui se transmet donc à toute la hiérarchie.