Les exceptions Java sont un mécanisme de cohérence fourni par Java pour identifier et répondre aux erreurs.
Le mécanisme d'exception Java peut séparer le code de gestion des exceptions dans le programme du code commercial normal, garantir que le code du programme est plus élégant et améliore la robustesse du programme. Lorsque vous utilisez efficacement les exceptions, l'exception peut clairement répondre à ces trois questions: le type d'exception répond "quoi" et la trace de pile d'exception répond "où" et les informations d'exception répondent "pourquoi" est lancée.
Plusieurs mots clés utilisés dans le mécanisme d'exception Java: essayez, attrapez, enfin, lance, lance.
| Mots clés | illustrer |
|---|---|
| essayer | Utilisé pour l'écoute. Mettez le code à écouter (code qui peut lancer une exception) dans le bloc d'essai. Lorsqu'une exception se produit dans le bloc de l'instruction TRY, l'exception est lancée. |
| attraper | Utilisé pour prendre des exceptions. Catch est utilisé pour attraper des exceptions qui se produisent dans les blocs de déclaration d'essai. |
| Enfin | Enfin, les blocs d'instruction seront toujours exécutés. Il est principalement utilisé pour recycler les ressources matérielles (telles que les connexions de base de données, les connexions réseau et les fichiers de disque) ouverts dans les blocs d'essai. Ce n'est qu'après que l'exécution du bloc final renvoie ou lancera des instructions dans le bloc d'essai ou de capture. Si les instructions finales telles que le retour ou le lancer sont utilisées, elles ne reculeront pas à l'exécution et s'arrêteront directement. |
| lancer | Utilisé pour lancer des exceptions. |
| lancers | Utilisé dans les signatures de méthode pour déclarer des exceptions qui peuvent être lancées par la méthode. |
classe publique Demo1 {public static void main (String [] args) {try {int i = 10/0; System.out.println ("i =" + i); } catch (arithmeticexception e) {System.out.println ("Exception capturée"); System.out.println ("e.getMessage ():" + e.getMessage ()); System.out.println ("e.toString ():" + e.toString ()); System.out.println ("E.PrintStackTrace ():"); e.printStackTrace (); }}} Résultats en cours:
Capturé exceptione.getMessage (): / par zeroe.toString (): java.lang.arithmeticexception: / by zeroe.printstacktrace (): java.lang.arithmeticexception: / par zéro à Demo1.Main (Demo1.java:6)
La description du résultat: il y a une opération avec un diviseur de 0 dans le bloc de déclaration d'essai, et l'opération lancera une exception java.lang.arithmeticexception. Par Catch, l'exception est capturée.
Observer les résultats que nous avons constatés que System.out.println ("i =" + i) n'a pas été exécuté. Cela signifie qu'après une exception dans le bloc de l'instruction TRY, le contenu restant dans le bloc d'instruction TRY ne sera plus exécuté.
Exemple 2: Comprendre l'utilisation de base de enfin
Sur la base de "Exemple un", nous ajoutons enfin des instructions.
classe publique Demo2 {public static void main (String [] args) {try {int i = 10/0; System.out.println ("i =" + i); } catch (arithmeticexception e) {System.out.println ("Exception capturée"); System.out.println ("e.getMessage ():" + e.getMessage ()); System.out.println ("e.toString ():" + e.toString ()); System.out.println ("E.PrintStackTrace ():"); e.printStackTrace (); } enfin {System.out.println ("Run enfin"); }}} Résultats en cours:
Capturé exceptione.getMessage (): / by zeroe.toString (): java.lang.arithmeticexception: / by zeroe.printstacktrace (): java.lang.arithmeticexception: / par zéro à Demo2.Main (Demo2.java:6) Running
RÉSULTATS: Enfin, le bloc de l'instruction a été exécuté.
Exemple 3: Comprendre l'utilisation de base des lancers et des lancers
Les lancers sont utilisés pour déclarer des exceptions lancées, tandis que Throw est utilisé pour lancer des exceptions.
classe MyException étend l'exception {public myException () {} public myException (String msg) {super (msg); }} public class Demo3 {public static void main (String [] args) {try {test (); } catch (myException e) {System.out.println ("Catch My Exception"); e.printStackTrace (); }} public static void test () lève myException {try {int i = 10/0; System.out.println ("i =" + i); } catch (arithmeticexception e) {throw new myException ("This is MyException"); }}} Résultats en cours:
Catch My ExceptionMyException: Ceci est MyException à Demo3.test (Demo3.java:24) à Demo3.Main (Demo3.java:13)
Résultats: MyException est une sous-classe héritée de l'exception. L'exception arithmeticexception (le diviseur est 0) est générée dans le bloc d'énoncé d'essai de test (), et l'exception est capturée dans la capture; Ensuite, une exception MyException est lancée. La méthode principale () capture la MyException lancée dans Test ().
Cadre d'exception Java
Diagramme d'architecture d'exception Java:
1. Jelable
Throwable est une superclasse de toutes les erreurs ou exceptions dans la langue java.
Throwable contient deux sous-classes: Erreur et exception. Ils sont généralement utilisés pour indiquer qu'une anomalie s'est produite.
Throwable contient des instantanés du thread exécutant la pile lorsque son thread est créé. Il fournit des interfaces telles que printStackTrace () pour obtenir des informations telles que les données de trace de pile.
2. Exception
L'exception et ses sous-classes sont une forme de jetable qui souligne les conditions qu'une application raisonnable souhaite capturer.
3. RuntimeException
RuntimeException est une superclasse qui peut lancer des exceptions pendant le fonctionnement normal de la machine virtuelle Java.
Le compilateur ne vérifie pas les exceptions RuntimeException. Par exemple, lorsque le diviseur est zéro, une exception ArithMeticexception est lancée. RuntimeException est une superclasse de ArithMeticexception. Lorsque le code a un diviseur de zéro, il peut également être compilé s'il "n'est pas jeté via la déclaration des lancers" ou "n'est pas géré via Try ... Catch ...". C'est ce que nous disons "Le compilateur ne vérifiera pas les exceptions de RuntimeException"!
Si le code génère une exception RuntimeException, il doit être évité en modifiant le code. Par exemple, si le diviseur est nul, vous devez éviter cette situation via le code!
4. Erreur
Comme l'exception, l'erreur est également une sous-classe de jetable. Il est utilisé pour indiquer de graves problèmes qu'une application raisonnable ne devrait pas tenter d'attraper, et la plupart des erreurs sont des conditions exceptionnelles.
Comme RuntimeException, le compilateur ne vérifiera pas une erreur.
Java divise la structure jetable en trois types: exception vérifiée (exception vérifiée), exception d'exécution (RuntimeException) et erreur (erreur).
(1) Exception d'exécution
Définition: RuntimeException et ses sous-classes sont appelées exceptions d'exécution.
Caractéristiques: Le compilateur Java ne le vérifiera pas. C'est-à-dire que lorsqu'une telle exception peut se produire dans le programme, si elle n'est pas lancée dans la déclaration des lancers "ou" non capturée avec l'instruction TRY-Catch ", elle sera toujours compilée et adoptée. Par exemple, l'exception ArithMeticexception générée lorsque le diviseur est nul, l'exception indexoutofboundSexception générée lorsque le tableau est hors limites, l'exception de concurrentModificationException généré par le mécanisme d'échec, etc., sont toutes des exceptions d'exécution.
Bien que le compilateur Java ne vérifie pas les exceptions d'exécution, nous pouvons également le déclarer et le lancer à travers des lancers, ou le capturer via un coup de main.
Si une exception d'exécution est générée, elle doit être évitée en modifiant le code. Par exemple, si le diviseur est nul, vous devez éviter cette situation via le code!
(2) Exception vérifiée
Définition: La classe d'exception elle-même et d'autres sous-classes des sous-classes de l'exception, à l'exception de "l'exception d'exécution", sont toutes considérées comme des exceptions vérifiées.
Caractéristiques: Le compilateur Java le vérifiera. De telles exceptions sont déclarées et jetées à travers des lancers ou capturées via l'essai, sinon elles ne peuvent pas être compilées. Par exemple, ClonenotsupportEdException est une exception vérifiée. Lorsqu'un objet est cloné via l'interface Clone () et que la classe correspondante de l'objet n'implémente pas l'interface clonable, un clonenotsupportEdException sera lancé.
Les exceptions en cours de vérification peuvent généralement être récupérées.
(3) erreur
Définition: classe d'erreur et ses sous-classes.
Caractéristiques: Comme les exceptions d'exécution, le compilateur ne vérifiera pas les erreurs.
Une erreur se produit en cas de ressources insuffisantes, de défaillance des contraintes ou d'autres conditions qui ne peuvent pas continuer à exécuter par d'autres programmes. Le programme lui-même ne peut pas corriger ces erreurs. Par exemple, VirtualMachineerror est une erreur.
Selon la convention Java, nous ne devons pas implémenter de nouvelles sous-classes d'erreur!
Pour les trois structures ci-dessus, laquelle devons-nous lancer une exception ou une erreur? La recommandation donnée dans "Java efficace" est: utiliser des exceptions vérifiées pour les conditions qui peuvent être récupérées et utiliser des exceptions d'exécution pour les erreurs de programme.
Plusieurs suggestions sur la gestion des exceptions
Article 1: Utilisez des exceptions uniquement pour des situations anormales
Recommandation: Les exceptions ne doivent être utilisées que pour des conditions anormales, et elles ne doivent jamais être utilisées pour les flux de contrôle normaux.
L'explication est faite en comparant les deux codes ci-dessous.
Code 1
essayez {int i = 0; while (true) {arr [i] = 0; i ++; }} catch (indexoutofboundSException e) {} code 2for (int i = 0; i <arr.length; i ++) {arr [i] = 0;} Le but des deux codes est d'itérer sur le tableau ARR et de définir la valeur de chaque élément dans le tableau sur 0. Le code 1 se termine par exception, qui semble très difficile à comprendre, le code 2 se termine par les limites du tableau. Nous devons éviter d'utiliser le code 1 pour trois raisons principales:
La conception originale des mécanismes d'exception est destinée aux situations anormales, donc peu d'implémentations JVM essaient d'optimiser leurs performances. Par conséquent, les frais généraux de création, de lancement et de capture des exceptions coûtent cher.
La mise en place du code dans les rendements TRY-Catch empêche le JVM d'implémenter certaines optimisations spécifiques qui auraient pu être effectuées.
Le schéma standard de traverser des tableaux ne conduit pas à des vérifications redondantes, et certaines implémentations JVM modernes les optimiseront.
En fait, les modes basés sur les exceptions sont beaucoup plus lents que les modes standard. Le code de test est le suivant:
classe publique CONSEIL1 {private static int [] arr = new int [] {1,2,3,4,5}; Taille privée statique statique = 10000; public static void main (String [] args) {long s1 = System.currenttimemillis (); pour (int i = 0; i <size; i ++) endByRange (arr); long e1 = System.currenttimemillis (); System.out.println ("EndByRange Time:" + (E1-S1) + "MS"); long s2 = System.currenttimemillis (); pour (int i = 0; i <size; i ++) endByException (arr); long e2 = system.currentTimeMillis (); System.out.println ("EndByException Time:" + (E2-S2) + "MS"); } // Traverse le tableau ARR: private static void endByException (int [] arr) {try {int i = 0; while (true) {arr [i] = 0; i ++; //System.out.println("endByRange: arr ["+ i +"] = "+ arr [i]); }} catch (indexOutofBoundSException e) {}} // transférer le tableau ARR: ENDBYRANGE STATIQUE STATIQUE PRIVATE (int [] arr) {for (int i = 0; i <arr.length; i ++) {arr [i] = 0; //System.out.println("endByException: arr ["+ i +"] = "+ arr [i]); }}} Résultats en cours:
EndByRange Temps: 8MSENDBYException Temps: 16 ms
Le résultat montre que la vitesse de traverser une exception est beaucoup plus lente que de traverser un tableau de manière ordinaire!
Article 2: Utilisez des exceptions vérifiées pour les conditions récupérables et utilisez des exceptions d'exécution pour les erreurs de programme.
| anormal | illustrer |
|---|---|
| Exception d'exécution | La classe RuntimeException et ses sous-classes sont appelées exceptions d'exécution. |
| L'exception vérifiée | La classe d'exception elle-même, ainsi que d'autres sous-classes dans l'exception, sauf "l'exception d'exécution" sont toutes des exceptions vérifiées. |
La différence est que le compilateur Java vérifie les "exceptions vérifiées" et ne vérifie pas les "exceptions d'exécution".
C'est-à-dire que pour l'exception vérifiée, elle est déclarée et jetée à travers des lancers, soit capturée via l'essai, sinon il ne peut pas être compilé. Pour les exceptions d'exécution, s'ils ne sont "pas jetés dans la déclaration des lancers" ou "non capturé avec une déclaration de couple d'essai", ils seront toujours compilés et adoptés. Bien sûr, bien que le compilateur Java ne vérifie pas les exceptions d'exécution, nous pouvons également expliquer l'exception via les lancers ou le capturer via l'essai.
RithMeticexception (par exemple, le diviseur est 0), indexoutofboundSexception (par exemple, array hors des limites), etc. sont toutes des exceptions d'exécution. Pour cette exception, nous devons l'éviter en modifiant le code. Pour l'exception vérifiée, le programme peut être restauré pour exécuter le traitement. Par exemple, supposons que parce qu'un utilisateur ne stockait pas un nombre suffisant d'appels, il échouera lorsqu'il tentera de passer un appel à un appel salarial; Jetant ainsi une exception vérifiée.
Article 3: Évitez l'utilisation inutile des exceptions vérifiées
"Exception censurée" est une bonne caractéristique de Java. Contrairement au code de retour, les "exceptions vérifiées" obligent le programmeur à gérer les conditions d'exception, améliorant considérablement la fiabilité du programme.
Cependant, une utilisation excessive des exceptions vérifiées peut rendre l'API très gênant. Si une méthode lance une ou plusieurs exceptions vérifiées, le code qui appelle la méthode doit gérer ces exceptions dans un ou plusieurs blocs d'instructions de capture, ou ils doivent être jetés via la déclaration des lancers. Qu'il soit géré par la capture ou le lancement de déclarations, il ajoute un fardeau ininvestible pour les programmeurs.
Deux conditions doivent être remplies pour les «exceptions vérifiées»: Premièrement, même si l'API est utilisée correctement, elle ne peut pas empêcher la survenue de conditions d'exception. Deuxièmement, une fois une exception générée, les programmeurs utilisant l'API peuvent prendre des mesures utiles pour traiter le programme.
Article 4: Essayez d'utiliser des exceptions standard
La réutilisation du code est digne de plaidoyer, il s'agit d'une règle courante et les exceptions ne font pas exception. Il y a plusieurs avantages à réutiliser les exceptions existantes:
Tout d'abord, cela rend votre API plus facile à apprendre et à utiliser car elle est cohérente avec les idiomes que les programmeurs se familiarisent.
Deuxièmement, pour les programmes qui utilisent ces API, ils sont mieux lisibles car ils ne sont pas remplis d'exceptions qui ne sont pas familières aux programmeurs.
Troisièmement, moins les classes d'exception, plus l'utilisation de la mémoire est petite, et plus le temps consacré à réimprimer ces classes est faible.
Plusieurs des exceptions standard Java sont souvent des exceptions utilisées. Le tableau suivant:
| anormal | Utiliser des occasions |
|---|---|
| IllégalargumentException | La valeur du paramètre n'est pas appropriée |
| IllégalstateException | Les paramètres ne sont pas inappropriés |
| NullPointerException | Lorsque null est désactivé, la valeur du paramètre est nul |
| IndexoutofboundSexception | L'indice traverse la frontière |
| ConcurrentModificationException | Lorsque la modification simultanée est interdite, l'objet détecte une modification simultanée |
| InsupportedOperationException | Méthodes que l'objet ne prend pas en charge les demandes des clients |
Bien qu'ils soient les exceptions les plus couramment réutilisées des bibliothèques de plate-forme Java à ce jour, d'autres exceptions peuvent également être réutilisées dans des conditions de licence. Par exemple, si vous souhaitez implémenter des objets arithmétiques tels que des nombres complexes ou des matrices, il serait très approprié de réutiliser ArithMeticexception et NumberFormatexception. Si une exception répond à vos besoins, n'hésitez pas à l'utiliser, mais vous devez vous assurer que l'état de lancer l'exception est conforme aux conditions décrites dans la documentation pour l'exception. Cette réutilisation doit être basée sur la sémantique, pas sur le nom!
Enfin, assurez-vous d'être clair qu'il n'y a pas de règle qui doit être suivie lors du choix de l'exception à réutiliser. Par exemple, compte tenu du cas d'un objet de carte, supposons qu'il existe une méthode pour traiter les opérations, et son paramètre (HandSize) est le nombre de cartes à donner dans une main. Supposons que l'appelant passe une valeur dans ce paramètre supérieur au nombre restant de cartes pour l'ensemble du jeu. Ensuite, cette situation peut être interprétée comme une conception illégalargumentException (la valeur de HandSize est trop grande) ou une illégalstateException (l'objet de la carte a trop peu de cartes par rapport à la demande du client).
Article 5: L'exception lancée doit convenir à l'abstraction correspondante
Cette situation peut être écrasante si une exception lancée par une méthode n'a aucune corrélation évidente avec la tâche qu'elle effectue. Cela se produit souvent lorsqu'une méthode passe une exception lancée par une abstraction de bas niveau. Lorsque cela se produit, il confond non seulement, mais "pollue" des API de haut niveau.
Pour éviter ce problème, la mise en œuvre de haut niveau doit attraper l'exception de bas niveau et lancer une exception qui peut être introduite en fonction de l'abstraction de haut niveau. Cette pratique est appelée «traduction d'exception».
Par exemple, la méthode Java Collection Framework AbstractSesentialList get () est la suivante (basée sur JDK1.7.0_40):
public e get (int index) {try {return listIterator (index) .next (); } catch (nosuchementElementException exc) {throw new indexOutofBoundSexception ("index:" + index); }}ListeTiterator (index) renvoie l'objet ListIterator. L'appel de la méthode suivante () de l'objet peut lancer une exception NosuchementElementException. Dans la méthode get (), le lancer d'une exception NosuchementException sera déroutant. Donc, get () capture NosuchementElementException et lance une exception IndexoutofBoundSexception. Autrement dit, il équivaut à convertir NosuchementElementException en indexoutofboundSexception Exception.
Article 6: L'exception lancée par chaque méthode doit être documentée
Pour déclarer l'exception vérifiée séparément et utiliser la balise @throws de Javadoc pour enregistrer avec précision les conditions pour chaque exception à lancer.
Si de nombreuses méthodes dans une classe lancent la même exception pour la même raison, il est acceptable de faire de la documentation pour cette exception dans les commentaires du document de cette classe, plutôt que de documenter individuellement pour chaque méthode.
Article 7: Inclure l'échec dans le message de capture de message en détail
En bref, lorsque nous personnalisons ou lançons une exception, nous devons inclure des informations liées à l'échec.
Lorsqu'un programme échoue en raison d'une exception non revêtue, le système imprimera automatiquement la trace de pile de l'exception. Contient une représentation de chaîne de l'exception dans la piste de pile. En règle générale, il contient le nom de classe de la classe d'exception, ainsi que le message détaillé qui suit.
Article 8: Efforcez-vous de maintenir l'atomicité en échec
Lorsqu'un objet lance une exception, nous nous attendons toujours à ce que l'objet reste dans un état disponible bien défini. Ceci est particulièrement important pour l'exception vérifiée, car l'appelant prévoit généralement de se remettre de l'exception vérifiée.
D'une manière générale, un appel de méthode défaillant devrait garder l'objet dans "son état avant qu'il ne soit appelé". Les méthodes avec de tels attributs sont appelées «échec atomique». On peut comprendre que l'échec maintient toujours l'atomicité. Il existe plusieurs façons pour un objet de maintenir "l'atomicité ratée":
(1) Concevoir un objet non mutable.
(2) Pour les méthodes qui effectuent des opérations sur des objets mutables, la façon la plus courante d'obtenir "l'atomicité échouée" est de vérifier la validité des paramètres avant d'effectuer l'opération. Comme suit (méthode pop dans stack.java):
objet public pop () {if (size == 0) lancez un nouveau videstacKException (); Résultat de l'objet = éléments [- taille]; éléments [taille] = null; Retour Résultat;} (3) Semblable à la méthode précédente, le traitement de calcul peut être ajusté de sorte que toute défaillance possible de la pièce de calcul se produit avant la modification de l'état de l'objet.
(4) Écrivez un code de récupération pour expliquer les échecs pendant l'opération et pour faire revenir l'objet à l'état avant le début de l'opération.
(5) Effectuez une opération sur une copie temporaire de l'objet, et une fois l'opération terminée, copiez le résultat par la copie temporaire dans l'objet d'origine.
Bien que "le maintien de l'atomicité ratée d'un objet" soit le but souhaité, il n'est pas toujours possible. Par exemple, si plusieurs threads tentent d'accéder à un objet simultanément sans mécanismes de synchronisation appropriés, l'objet peut être laissé dans un état incohérent.
Même dans les situations où «l'atomicité échouée» peut être réalisée, il n'est pas toujours prévu. Pour certaines opérations, il peut augmenter considérablement les frais généraux ou la complexité.
La règle générale est: dans le cadre de la spécification de la méthode, aucune exception ne devrait modifier l'état de l'objet avant d'appeler la méthode. Si cette règle est violée, la documentation de l'API doit indiquer clairement dans quel état sera l'objet.
Article 9: N'ignorez pas les exceptions
Lorsqu'un concepteur API déclare qu'une méthode lancera une exception, il essaie d'illustrer quelque chose. Alors, ne l'ignorez pas! Le code pour ignorer les exceptions est le suivant:
essayez {...} catch (SomeException e) {} Un bloc de capture vide fera que l'exception échoue pour atteindre son objectif durable. Le but de l'exception est de vous forcer à faire face à des conditions anormales. Ignorer une exception, c'est comme ignorer un signal d'alarme incendie - si le signal d'alarme d'incendie est désactivé, personne ne verra le signal d'alarme incendie lorsque le véritable incendie se produit. Ainsi, au moins le bloc de capture doit contenir une description qui explique pourquoi il est approprié d'ignorer cette exception.