Les différentes exceptions
La classe mère de toutes les exceptions est la classe java.lang.Throwable et ses deux sous-classes directes sont
- java.lang.Error : erreur technique grave qui ne devrait jamais être catchée dans votre code mais plutôt remontée jusqu’à la JVM
- java.lang.Exception : erreur fonctionnelle devant être gérée par votre application.
Si vous attrapez les erreurs Throwable comme je le montre ici, vous attrapez toutes les erreurs qu’elles soient fonctionnelles ou techniques et vous pouvez priver la JVM de toutes les alertes de type Error qui lui permettront d’exécuter de manière intègre votre application (out of memory, problème de vérification du bytecode…) ⇒ Il faut à tout prix proscrire l’utilisation de catch Throwable dans vos applications
try{ ... } cath(Throwable e){ e.printStackTrace(); }
Checked et unchecked exception
La classe RuntimeException hérite de la classe Exception et sa particularité est de ne pas être “checkée” à la compilation. . On parle d’unchecked exceptions. Vous n’avez pas besoin de déclarer l’exception dans la signature de vos méthodes ou de la catcher avant la sortie de la méthode
Exemple avec une unchecked exception
public void maMethode() { throw new RuntimeException("Mon exception"); }
Exemples avec une checked exception
public void maMethode() throws Exception{ throw new Exception("Mon exception"); }
ou
public void maMethode() { try{ throw new Exception(“Mon exception”); } catch(Exception e){ e.printStackTrace(); } }Les exceptions de type Error décrites plus haut sont aussi des exceptions unchecked. Maintenant que je vous ai donné des exemples je vais vous déconseiller de les utiliser…
Pourquoi ne devrions nous jamais attraper les exceptions Exception ou RunTimeException mais seulement leurs classes filles ?
Imaginez vous, lorsque vous êtes malade et que vous allez chez votre médecin.
Vous : "Docteur j’ai une exception que pouvez vous pour moi?"
Le doc : "Ben on va pas prendre de risque on va faire une chimio comme ça on traitera tous les problèmes… "
En Java c’est un peu la même chose il n’existe pas une manière unique de traiter un problème. Comme pour une maladie il faut identifier le problème et le meilleur moyen est d’avoir une identification claire pour ensuite la traiter efficacement.
Le langage vous fournit un grand nombre de classes d’exceptions que vous pouvez utiliser.
checked exception :
AclNotFoundException, ActivationException, AlreadyBoundException, ApplicationException, AWTException, BackingStoreException, BadAttributeValueExpException, BadBinaryOpValueExpException, BadLocationException, BadStringOperationException, BrokenBarrierException, CertificateException,CloneNotSupportedException, DataFormatException, DatatypeConfigurationException, DestroyFailedException, ExecutionException, ExpandVetoException, FontFormatException, GeneralSecurityException, GSSException, IllegalClassFormatException, InterruptedException, IntrospectionException, InvalidApplicationException,InvalidMidiDataException, InvalidPreferencesFormatException, InvalidTargetObjectTypeException, IOException, JAXBException, JMException, KeySelectorException, LastOwnerException, LineUnavailableException, MarshalException, MidiUnavailableException, MimeTypeParseException, MimeTypeParseException,NamingException, NoninvertibleTransformException, NotBoundException, NotOwnerException, ParseException, ParserConfigurationException, PrinterException, PrintException, PrivilegedActionException, PropertyVetoException, ReflectiveOperationException, RefreshFailedException, RemarshalException,SAXException, ScriptException, ServerNotActiveException, SOAPException, SQLException, TimeoutException, TooManyListenersException, TransformerException, TransformException, UnmodifiableClassException, UnsupportedAudioFileException, UnsupportedCallbackException, UnsupportedFlavorException,UnsupportedLookAndFeelException, URIReferenceException, URISyntaxException, UserException, XAException, XMLParseException, XMLSignatureException, XMLStreamException, XPathException
unchecked exception :
AnnotationTypeMismatchException, ArithmeticException, ArrayStoreException, BufferOverflowException, BufferUnderflowException, CannotRedoException, CannotUndoException, ClassCastException, CMMException, ConcurrentModificationException, DataBindingException, DOMException, EmptyStackException,EnumConstantNotPresentException, EventException, FileSystemAlreadyExistsException, FileSystemNotFoundException, IllegalArgumentException, IllegalMonitorStateException, IllegalPathStateException, IllegalStateException, IllformedLocaleException, ImagingOpException, IncompleteAnnotationException,IndexOutOfBoundsException, JMRuntimeException, LSException, MalformedParameterizedTypeException, MirroredTypesException, MissingResourceException, NegativeArraySizeException, NoSuchElementException, NoSuchMechanismException, NullPointerException, ProfileDataException, ProviderException,ProviderNotFoundException, RasterFormatException, RejectedExecutionException, SecurityException, SystemException, TypeConstraintException, TypeNotPresentException, UndeclaredThrowableException, UnknownEntityException, UnmodifiableSetException, UnsupportedOperationException, WebServiceException,WrongMethodTypeException
Lorsque vous êtes dans un traitement métier qui vous est propre et que ces exceptions ne sont pas adaptées, vous pouvez créer les votres. . Si je reprends l’exemple du patient et d’une application qui permettrait de définir la maladie en fonction des symptômes on pourrait avoir des exceptions du type SinusiteException, PharyngiteException, GrippeException….
Personnaliser vos exceptions vous permet d’avoir des piles d’erreur plus lisibles et plus compréhensibles. Pour améliorer la lisibilité il est aussi fortement conseillé
- d’ajouter un message quand vous déclarez une exception
- de propager les exceptions si vous êtes dans un bloc catch afin de ne pas perdre la pile d’exception
Tester les paramètres de méthodes
Si vous avez une méthode avec des paramètres il est préférable de les tester.
Prenons un exemple
public Maladie definirMaladieEnFonctionDesSymptomes(Malade malade, Symptome … symptomes);
J’ai souvent entendu la phrase suivante "Mais pourquoi tester si un paramètre est null ? De toute façon s’il est null on aura une NullPointerException…."
Oui sauf que le message affiché dans la log sera
Exception in thread "main" java.lang.NullPointerException
at Main.main(Main.java:6)
Sans les sources du programme, il est difficile de savoir ce qu’il se passe en ligne 6 de la classe Main…. Alors que si en début de méthode vous ajoutez
if(malade==null){ throw new NullPointerException("Le malade est obligatoire"); }
vous aurez
Exception in thread "main" java.lang.NullPointerException: Le malade est obligatoire
at Main.main(Main.java:3)
Sans regarder les sources du programme je sais pourquoi, ma fonction ne fonctionne pas.
Une autre remarque que l’on m’a fait : "Mais si tu testes à chaque fois tous les paramètres, tes fonctions comprenennt un grand nombre de blocs conditionnels qui nuisent à la lisibilité et imposent l’écriture de nombreux tests pour ne pas baisser la couverture du code par les tests…."
La réponse est non car vous pouvez utiliser des librairies utilitaires comme Guava.
Par exemple
Preconditions.checkNotNull(malade, "Le malade est obligatoire"); Preconditions.checkArgument(args.length>0, "La liste des symptomes est obligatoire pour le patient" + malade.getNom());
La première ligne génère un NullPointerException si le malade n’est pas renseigné et la deuxième une IllegalArgumentException avec à chaque fois un message clair et précis.
Faut il utiliser des checked exceptions ou des unchecked exceptions ?
Sur ce point il y a deux écoles.
La préconisation Java est de dire qu’il est préférable d’utiliser des checked exceptions afin de mettre à disposition des API donnant dans leur signature les types d’exception qui sont susceptibles d'être déclenchés.
Par exemple dans la classe java.io.File
public boolean createNewFile() throws IOException
Mais procéder de cette manière pollue vos API et vous impose de toujours remonter ou transformer les exceptions qui sont déclenchées. Spring ou Hibernate ne proposent que des unchecked exception. Je suis d’accord qu’une API doit préciser quelles exceptions sont susceptibles d’être déclenchées mais vous pouvez le faire via la javadoc et l’annotation @throws.
Par exemple si on regarde le constructeur de la classe File
/** Creates a new File instance by converting the given file: URI into an abstract pathname. * The exact form of a file: URI is system-dependent, hence the transformation performed by this constructor is also system-dependent. * For a given abstract pathname f it is guaranteed that * new File( f.toURI()).equals( f.getAbsoluteFile()) * so long as the original abstract pathname, the URI, and the new abstract pathname are all created in (possibly different invocations * of) the same Java virtual machine. This relationship typically does not hold, * however, when a file: URI that is created in a virtual machine on one operating system is converted into an abstract pathname in a * virtual machine on a different operating system. * * @param uri An absolute, hierarchical URI with a scheme equal to "file", a non-empty path component, and undefined authority, query, and fragment components * @throws java.lang.NullPointerException If uri is null * @throws java.lang.IllegalArgumentException If the preconditions on the parameter do not hold * @since 1.4 */ public File(URI uri) { //…. }
Personnellement je suis un adepte du code simple et le plus lisible possible. C'est pourquoi je préfère utiliser des unchecked exception.
Le compromis est d’utiliser des checked exception lorsque vous voulez que les méthodes appelantes fassent un traitement particulier sur une exception et des unchecked exceptions lorsque les problèmes doivent remonter plus haut.
Aucun commentaire:
Enregistrer un commentaire
Remarque : Seul un membre de ce blog est autorisé à enregistrer un commentaire.