Ceci est un premier jet....c'est donc très perfectible.
A noter, on peut trouver des conneries aussi
PLAN :
I - Documentation
II - Une exception c'est quoi ?
III - A quoi ca sert ?
IV - Comment traiter une exception ?
V - Comment les utiliser ?
VI - Comment lire un stacktrace ?
VII - Les exceptions classiques ?
Convention :
les liens hypertextes sont formattés comme ceci
I - Documentation
Le tutorial de Sun propose une section très complète sur les exceptions
Java Tutorial - Essentials concepts - Exceptions (en anglais)
II - Une exception c'est quoi ?
Simplement, une exception est un méchanisme pour faire "remonter" les problèmes qui surviennent dans un programme.
Dans une application, 2 types de problemes peuvent survenir :
- les problèmes graves, qui rendent l'environnement Java instable : ce sont des problèmes irrécupérables
- les problèmes mineurs, qui n'affectent pas l'environnement. Ce sont des problemes raisonnablement prédictibles, qui si on code correctement, NE DOIVENT PAS entrainer l'arret du programme
Avant de donner un exemple, je dois encore préciser que les exceptions sont utilisables par le programmeur pour notifier les erreurs, et que c'est un méchanisme très puissant et très propre de programmation.
Une exception est un objet java, qui encapsule :
- le stacktrace, qui est la liste des dernières méthodes appellées avant que l'exception se produise. C'est en général ce truc rouge qui s'affiche dans la console quand une erreur arrive.
Exemple :
Code :
- Error while parsing
- java.text.ParseException: Format.parseObject(String) failed
- at java.text.Format.parseObject(Unknown Source)
- at com.jubijub.ClasseDeTest.main(ClasseDeTest.java:33)
|
J'expliquerai plus tard comment lire un stacktrace.
- éventuellement, un objet String contenant un message d'erreur
III - A quoi ca sert ?
Prenons un exemple :
Une partie de votre programme doit lire un fichier. Il est parfaitement possible qu'un fichier n'existe pas ou plus, ou qu'il soit illisible, ou verrouillé, pour X raisons. Un programme de ce type doit donc toujours savoir quoi faire si le fichier n'est pas lisible. C'est une erreur raisonnablement prévisible, et il n'y a pas de raison qu'elle entraine l'arret de l'application. De plus, le problème est relativement cloisonné, et n'affecte pas le reste de l'application. Par exemple il vaut mieux, si l'évènement arrive, proposer de chercher un autre fichier.
Les exceptions permettent :
1) de canaliser les erreurs
2) de déterminer quelle partie de l'application doit traiter l'erreur, et donc comment la traiter
IV - Comment traiter une exception ?
Je développerai ce point plus tard, mais pour le moment, il faut juste savoir qu'une méthode toto() peut être déclarée comme "jetant une exception", ce qui veut dire qu'elle est susceptible de générer une exception.
A partir de là, toute méthode appellant toto() a le choix entre :
- prendre en charge l'exception
- la "propager" au niveau supérieur, c'est à dire se contenter de la relancer (concept de la patate chaude)
Exemple : la méthode baba() appelle la méthode bibi() qui appelle la méthode bobo() qui jette une exception.
- bibi peut catcher l'exception de bobo, ou la relancer
- ce qu'on doit faire dans baba dépend de bibi : si bibi a catché l'exception, baba n'a rien à faire. Sinon bibi a propagé, baba doit catcher l'exception. Si baba relance l'exception, ce sera aux méthode appelant baba() de catcher l'exception.
Si personne ne catche une exception, et qu'elle est propagée tout le temps, elle va sauter à la tete de l'utilisateur, ce qui n'est évidement pas propre.
En imagé, une exception c'est comme une petite bombe : soit on la désamorce ("on la catche" ), soit on la refile au voisin (on la "propage" ). Si personne la désamorce, elle pète à la gueule de l'utilisateur.
==> il faut donc toujours catcher une exception à un moment ou à un autre
Catcher une exception :
Prenons l'exemple de bibi() qui appelle bobo() qui jette une exception
Code :
- public void bibi() {
- try { // début de la zone où peut se produire l'exception
- ObjetBidon bidon = bobo();
- } catch (BoboException be) { // fin de la zone, début de la zone catch, qui indique quoi faire si exception
- System.out.println("Insérer ici un message informatif" );
- be.printStackTrace(); //utile en débug : permet d'afficher la trace
- } // fin de la zone catch
- }
|
Il y a plusieurs règles à observer :
- ne *** JAMAIS *** faire un catch & bury :
Le catch & bury c'est ça :
Code :
- try {
- bobo();
- } catch (Exception e) {
- // ne rien faire
- }
|
Il est très très très très rare d'avoir à faire ça pour une raison valable...traitez toujours vos exception : logs, remonté du message, procédure pour réinitialiser votre modèle, etc...
- Essayez de séparer vos exceptions : en effet, une méthode peut lancer plusieurs exceptions, et a fortiori un bloc de code. Essayez de ne pas catcher exception d'un bloc (catch Exception e) mais de catcher chaque exception séparément. Ca améliore la lisibilité du code, ca vous permet de faire des traitements différenciés en fonction de l'erreur. Là c'est à vous de voir, mais c'est plus propre.
- Pour un fini plus propre de l'application, débrouillez vous catcher toutes vos exceptions...
V - Comment les utiliser ?
Utiliser le méchanisme des exception est certes un peu contraignant (il faut décider où "catcher" l'exception, gérer un finally eventuel, etc...) mais en contrepartie, c'est un méchanisme très souple est très propre pour gérer la remontée d'un problème.
Prenons un exemple
Vous construisez une application de gestion de timbres. Chaque timbre dispose d'une cote, que vous souhaitez renseigner.
Vous proposez un écran de saisie d'un nouveau timbre. Vous DEVEZ controler que les données saisies sont correctes. Par exemple, une cote étant un prix, elle ne peut être négative.
Dans votre modèle, vous avez un objet Timbre, avec un attribut cote de type double, et un setCote(double cote) et getCote();
Conceptuellement, votre modèle doit garantir son intégrité, c'est à dire que Timbre doit garantir que les informations qu'on lui fournit sont cohérentes.
On peut soit utiliser une Exception existante, soit créer la sienne propre.
par exemple, la méthode setCote(double cote) pourrait ressembler à ceci :
Imaginons qu'on utilise une exception customizée appellée InvalidPriceException
Code :
- public void setCote(double cote) throws InvalidPriceException {
- if (cote < 0) {
- throw new InvalidPriceException("La cote spécifiée ne peut être nulle" );
- } else {
- this.cote = cote;
- }
|
Le message d'erreur permet de préciser l'exception.
N'utilisez les exceptions que pour une bonne raison. Parfois un if/else peut remplacer une exception. La limite est floue est sujette à débat. Posez vous la question : est-ce que sémantiquement, c'est une erreur au sens de mon application ?
Parfois vous serez obligé d'utiliser une exception, parce que java vous l'impose (méthode du package Java.IO, parsing, etc...).
VI - Comment lire un stacktrace ?
Au secours !!!! J'ai une exception et je comprends pas pourquoi.
Un stacktrace est composé comme suit :
- La première ligne donne le type de l'exception, et le message d'erreur lié
- 1 à n lignes représentant la pile d'appel des méthodes : c'est écrit dans l'ordre décroissant : la dernière méthode appellée est écrite en haut, la première en bas de la liste. Si la source est disponible, la ligne affiche également le fichier java et la ligne où figure la méthode concernée. Si vous utilisez un IDE, un clic sur la ligne vous amène généralement au lieu concerné.
- éventuellement, le thread où s'est passé l'exception
Prenons le programme suivant (il est débile, mais c'est pour le test)
Note : l'exception ici renvoyée est une fille de RuntimeException, qui a de ce fait n'a pas forcément à etre catché. Pour les RuntimeException, le catch est facultatif. Les NullPointerException font aussi partie de cette famille.
Code :
- public class TesTest {
- public TesTest() {
- }
- public void methodeInutile(String param) {
- if (param.length() <1) {
- throw new IllegalArgumentException("Le paramètre a une longueur nulle" );
- }
- }
- public static void main(String[] args) {
- TesTest test = new TesTest();
- test.methodeInutile("" );
- }
- }
|
Imaginons le stacktrace suivant :
Code :
- java.lang.IllegalArgumentException: Le paramètre a une longueur nulle
- at com.jubijub.TesTest.methodeInutile(TesTest.java:11)
- at com.jubijub.TesTest.main(TesTest.java:17)
- Exception in thread "main"
|
On voit :
- que c'est une java.lang.IllegalArgumentException (on peut donc commencer par aller voir la javadoc de l'exception pour comprendre ce qu'elle signifie, ca aide bcp à trouver le contexte)
- la pile contient 2 appels. D'après le source, on voit que le main a été appellé, puis la méthodeInutile .
- le dernier appel étant méthodeInutile(), il y a de fortes chances que la source de l'exception soit là.
En examinant son code, on voit qu'effectivement elle est susceptible de lancer une exception. En regardant le code, on voit que si le param est une string vide ("" ), l'exception est lancée.
On peut donc affirmer que la methodeInutile a reçu "" comme paramètre. En obervant l'appel, on se rend compte que c'est la cas (ici c'est hardcodé, mais si c'était dynamique, il faudrait alors trouver pourquoi la String est vide)
Un peu plus dur
Code :
- [10/06/05 09:33:03:781 CEST] 64cf64cf SystemErr R java.lang.NullPointerException
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at java.lang.Throwable.<init>(Throwable.java)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at java.lang.Throwable.<init>(Throwable.java)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at java.lang.NullPointerException.<init>(NullPointerException.java)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at com.PouetPouet.command.UpdateUserFromFormCommand.performExecute(UpdateUserFromFormCommand.java:97)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at com.PouetPouet.framework.command.LocalTarget.executeCommand(LocalTarget.java:23)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at com.PouetPouet.framework.command.TargetableCommandImpl.execute(TargetableCommandImpl.java:40)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at com.PouetPouet.j20leanadm.web.action.usersManagement.UpdateUserAction.myExecute(UpdateUserAction.java:59)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at com.PouetPouet.j20leanadm.web.action.AbstractAction.performExecute(AbstractAction.java:42)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at com.PouetPouet.framework.web.struts.action.VolvoITAction.execute(VolvoITAction.java:41)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:484)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:274)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1482)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:525)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at javax.servlet.http.HttpServlet.service(HttpServlet.java:760)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at com.ibm.ws.webcontainer.servlet.StrictServletInstance.doService(StrictServletInstance.java:110)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at com.ibm.ws.webcontainer.servlet.StrictLifecycleServlet._service(StrictLifecycleServlet.java:174)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at com.ibm.ws.webcontainer.servlet.IdleServletState.service(StrictLifecycleServlet.java:313)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at com.ibm.ws.webcontainer.servlet.StrictLifecycleServlet.service(StrictLifecycleServlet.java:116)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at com.ibm.ws.webcontainer.servlet.ServletInstance.service(ServletInstance.java:283)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at com.ibm.ws.webcontainer.servlet.ValidServletReferenceState.dispatch(ValidServletReferenceState.java:42)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at com.ibm.ws.webcontainer.servlet.ServletInstanceReference.dispatch(ServletInstanceReference.java:40)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at com.ibm.ws.webcontainer.webapp.WebAppRequestDispatcher.handleWebAppDispatch(WebAppRequestDispatcher.java)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at com.ibm.ws.webcontainer.webapp.WebAppRequestDispatcher.dispatch(WebAppRequestDispatcher.java)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at com.ibm.ws.webcontainer.webapp.WebAppRequestDispatcher.forward(WebAppRequestDispatcher.java)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at com.ibm.ws.webcontainer.srt.WebAppInvoker.doForward(WebAppInvoker.java:79)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at com.ibm.ws.webcontainer.srt.WebAppInvoker.handleInvocationHook(WebAppInvoker.java:201)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at com.ibm.ws.webcontainer.cache.invocation.CachedInvocation.handleInvocation(CachedInvocation.java:71)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at com.ibm.ws.webcontainer.cache.invocation.CacheableInvocationContext.invoke(CacheableInvocationContext.java:114)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at com.ibm.ws.webcontainer.srp.ServletRequestProcessor.dispatchByURI(ServletRequestProcessor.java:186)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at com.ibm.ws.webcontainer.oselistener.OSEListenerDispatcher.service(OSEListener.java:334)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at com.ibm.ws.webcontainer.http.HttpConnection.handleRequest(HttpConnection.java:56)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at com.ibm.ws.http.HttpConnection.readAndHandleRequest(HttpConnection.java:610)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at com.ibm.ws.http.HttpConnection.run(HttpConnection.java)
- [10/06/05 09:33:03:796 CEST] 64cf64cf SystemErr R at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java)
|
Comment s'y retrouver ?
La pile est énorme ici.
Première chose, regarder les packages :
y'a du java.lang, y'a du com.PouetPouet, y'a du org.apache, et y'a du com.ibm ...on peut espérer que le problème ne vienne pas des outils utilisés, et que ca vient de notre code. On peut déjà restreindre à com.PouetPouet.
Il suffit alors de suivre les appels du plus récent au plus ancien. Attention, la lecture est ici obscurcie du fait d'une hiérarchie de classe (y'a le type Struts + 3 surtypes en dessous)...
VII - Les exceptions classiques ?
La NullPointerException (appellée "NPE" )
Cette exception, très fréquente, indique que l'on a tenté d'accéder à une méthode ou un champs d'un objet ayant une valeur nulle.
Par exemple :
Code :
- User user = null;
- System.out.println(user.getName());
|
user vaut null. On essaye d'appeller une méthode sur un objet à null. Ca va générer une nullPointerException.
Comment la résoudre :
Imaginons :
Code :
- System.out.println(user.getProfile().getFamilly().getSuperFamilly().getId());
|
Si cette ligne renvoit une NPE, ca peut etre :
- parce que user est null
- parce que le profile est null
- parce que la profile family est nulle
- parce que la superFamily est nulle
Il vous faudra donc tester patiemment pour trouver lequel est nul.
---------------
Jubi Photos : Flickr - 500px