1. Types d'exceptions

Types d'exceptions

Toutes les exceptions sont divisées en 4 types, qui sont en fait des classes qui héritent les unes des autres.

Throwableclasse

La classe de base pour toutes les exceptions est la Throwableclasse. La Throwableclasse contient le code qui écrit la pile d'appels actuelle (trace de pile de la méthode actuelle) dans un tableau. Nous apprendrons ce qu'est une trace de pile un peu plus tard.

L' opérateur throw ne peut accepter qu'un objet qui dérive de la Throwableclasse. Et bien que vous puissiez théoriquement écrire du code comme throw new Throwable();, personne ne le fait généralement. L'objectif principal de la Throwableclasse est d'avoir une classe parent unique pour toutes les exceptions.

Errorclasse

La classe d'exception suivante est la Errorclasse, qui hérite directement de la Throwableclasse. La machine Java crée des objets de la Errorclasse (et de ses descendants) lorsque des problèmes sérieux sont survenus . Par exemple, un dysfonctionnement matériel, une mémoire insuffisante, etc.

Habituellement, en tant que programmeur, il n'y a rien que vous puissiez faire dans une situation où une telle erreur (le type pour lequel un Errordoit être lancé) s'est produite dans le programme : ces erreurs sont trop graves. Tout ce que vous pouvez faire est d'informer l'utilisateur que le programme plante et/ou d'écrire toutes les informations connues sur l'erreur dans le journal du programme.

Exceptionclasse

Les classes Exceptionet RuntimeExceptionsont destinées aux erreurs courantes qui se produisent lors du fonctionnement de nombreuses méthodes. Le but de chaque exception levée est d'être attrapé par un catchbloc qui sait comment le gérer correctement.

Lorsqu'une méthode ne peut pas terminer son travail pour une raison quelconque, elle doit immédiatement en informer la méthode appelante en levant une exception du type approprié.

En d'autres termes, si une variable est égale à null, la méthode lancera un NullPointerException. Si les arguments incorrects ont été passés à la méthode, elle lancera un InvalidArgumentException. Si la méthode divise accidentellement par zéro, elle lancera un ArithmeticException.

RuntimeExceptionclasse

RuntimeExceptionssont un sous-ensemble de Exceptions. Nous pourrions même dire qu'il RuntimeExceptions'agit d'une version allégée des exceptions ordinaires ( Exception) — moins d'exigences et de restrictions sont imposées à ces exceptions

Vous apprendrez la différence entre Exceptionet RuntimeExceptionplus tard.


2. Throws: exceptions vérifiées

Lève : exceptions vérifiées

Toutes les exceptions Java appartiennent à 2 catégories : cochées et non cochées .

Toutes les exceptions qui héritent de RuntimeExceptionou Errorsont considérées comme des exceptions non contrôlées . Tous les autres sont des exceptions vérifiées .

Important!

Vingt ans après l'introduction des exceptions vérifiées, presque tous les programmeurs Java considèrent cela comme un bogue. Dans les frameworks modernes populaires, 95 % de toutes les exceptions ne sont pas contrôlées. Le langage C#, qui copiait presque exactement Java, n'ajoutait pas d'exceptions vérifiées .

Quelle est la principale différence entre les exceptions vérifiées et non vérifiées ?

Des exigences supplémentaires sont imposées aux exceptions vérifiées . Grosso modo, ce sont celles-ci :

Exigence 1

Si une méthode lève une exception cochée , elle doit indiquer le type d'exception dans sa signature . De cette façon, chaque méthode qui l'appelle est consciente que cette "exception significative" peut s'y produire.

Indiquez les exceptions cochées après les paramètres de la méthode après le throwsmot-clé (n'utilisez pas le throwmot-clé par erreur). Cela ressemble à ceci :

type method (parameters) throws exception

Exemple:

exception vérifiée exception non vérifiée
public void calculate(int n) throws Exception
{
   if (n == 0)
      throw new Exception("n is null!");
}
public void calculate(n)
{
   if (n == 0)
      throw new RuntimeException("n is null!");
}

Dans l'exemple de droite, notre code lève une exception non contrôlée — aucune action supplémentaire n'est requise. Dans l'exemple de gauche, la méthode lève une exception vérifiée , de sorte que le throwsmot-clé est ajouté à la signature de la méthode avec le type de l'exception.

Si une méthode s'attend à lever plusieurs exceptions vérifiées , toutes doivent être spécifiées après le throwsmot-clé, séparées par des virgules. L'ordre n'est pas important. Exemple:

public void calculate(int n) throws Exception, IOException
{
   if (n == 0)
      throw new Exception("n is null!");
   if (n == 1)
      throw new IOException("n is 1");
}

Exigence 2

Si vous appelez une méthode qui a vérifié les exceptions dans sa signature, vous ne pouvez pas ignorer le fait qu'elle les lève.

Vous devez soit intercepter toutes ces exceptions en ajoutant catchdes blocs pour chacune, soit en les ajoutant à une throwsclause de votre méthode.

C'est comme si nous disions: " Ces exceptions sont si importantes que nous devons les attraper. Et si nous ne savons pas comment les gérer, alors quiconque pourrait appeler notre méthode doit être informé que de telles exceptions peuvent s'y produire.

Exemple:

Imaginez que nous écrivions une méthode pour créer un monde peuplé d'humains. Le nombre initial de personnes est passé en argument. Nous devons donc ajouter des exceptions s'il y a trop peu de monde.

Créer la Terre Note
public void createWorld(int n) throws EmptyWorldException, LonelyWorldException
{
   if (n == 0)
      throw new EmptyWorldException("There are no people!");
   if (n == 1)
      throw new LonelyWorldException ("There aren't enough people!");
   System.out.println("A wonderful world was created. Population: " + n);
}
La méthode lève potentiellement deux exceptions vérifiées :

  • EmptyWorldException
  • LonelyWorldException

Cet appel de méthode peut être géré de 3 manières :

1. N'attrapez aucune exception

Cela se fait le plus souvent lorsque la méthode ne sait pas gérer correctement la situation.

Code Note
public void createPopulatedWorld(int population)
throws EmptyWorldException, LonelyWorldException
{
   createWorld(population);
}
La méthode appelante n'attrape pas les exceptions et doit en informer les autres : elle les ajoute à sa propre throwsclause

2. Attrapez certaines des exceptions

Nous gérons les erreurs que nous pouvons gérer. Mais ceux que nous ne comprenons pas, nous les renvoyons à la méthode appelante. Pour ce faire, nous devons ajouter leur nom à la clause throws :

Code Note
public void createNonEmptyWorld(int population)
throws EmptyWorldException
{
   try
   {
      createWorld(population);
   }
   catch (LonelyWorldException e)
   {
      e.printStackTrace();
   }
}
L'appelant n'attrape qu'une seule exception vérifiéeLonelyWorldException — . L'autre exception doit être ajoutée à sa signature en l'indiquant après le throwsmot clé

3. Attrapez toutes les exceptions

Si la méthode ne lève pas d'exceptions à la méthode appelante, alors la méthode appelante est toujours convaincue que tout a bien fonctionné. Et il ne pourra prendre aucune mesure pour remédier à une situation exceptionnelle.

Code Note
public void createAnyWorld(int population)
{
   try
   {
      createWorld(population);
   }
   catch (LonelyWorldException e)
   {
      e.printStackTrace();
   }
   catch (EmptyWorldException e)
   {
      e.printStackTrace();
   }
}
Toutes les exceptions sont capturées dans cette méthode. L'appelant sera convaincu que tout s'est bien passé.


3. Emballage des exceptions

Les exceptions vérifiées semblaient cool en théorie, mais se sont avérées être une énorme frustration dans la pratique.

Supposons que vous ayez une méthode super populaire dans votre projet. Il est appelé depuis des centaines d'endroits dans votre programme. Et vous décidez d' y ajouter une nouvelle exception vérifiée . Et il se peut que cette exception vérifiée soit vraiment importante et si spéciale que seule la main()méthode sait quoi faire si elle est interceptée.

Cela signifie que vous devrez ajouter l' exception vérifiéethrows à la clause de chaque méthode qui appelle votre méthode super populaire . Ainsi que dans la throwsclause de toutes les méthodes qui appellent ces méthodes. Et des méthodes qui appellent ces méthodes.

En conséquence, les throwsclauses de la moitié des méthodes du projet obtiennent une nouvelle exception vérifiée . Et bien sûr, votre projet est couvert par des tests, et maintenant les tests ne compilent pas. Et maintenant, vous devez également modifier les clauses throws dans vos tests.

Et puis tout votre code (tous les changements dans des centaines de fichiers) devra être revu par d'autres programmeurs. Et à ce stade, nous nous demandons pourquoi nous avons apporté tant de changements sanglants au projet ? Jour(s) de travail et tests cassés - tout cela pour ajouter une exception vérifiée ?

Et bien sûr, il y a encore des problèmes liés à l'héritage et au remplacement de méthode. Les problèmes qui proviennent des exceptions vérifiées sont beaucoup plus importants que les avantages. L'essentiel est que maintenant peu de gens les aiment et peu de gens les utilisent.

Cependant, il y a encore beaucoup de code (y compris le code de la bibliothèque Java standard) qui contient ces exceptions vérifiées . Que faire d'eux ? Nous ne pouvons pas les ignorer et nous ne savons pas comment les gérer.

Les programmeurs Java ont proposé d'encapsuler les exceptions vérifiées dans RuntimeException. En d'autres termes, interceptez toutes les exceptions cochées , puis créez des exceptions non cochées (par exemple, RuntimeException) et lancez-les à la place. Faire cela ressemble à ceci:

try
{
   // Code where a checked exception might occur
}
catch(Exception exp)
{
   throw new RuntimeException(exp);
}

Ce n'est pas une très jolie solution, mais il n'y a rien de criminel ici : l'exception a simplement été insérée dans un fichier RuntimeException.

Si vous le souhaitez, vous pouvez facilement le récupérer à partir de là. Exemple:

Code Note
try
{
   // Code where we wrap the checked exception
   // in a RuntimeException
}
catch(RuntimeException e)
{
   Throwable cause = e.getCause();
   if (cause instanceof Exception)
   {
      Exception exp = (Exception) cause;
      // Exception handling code goes here
   }
}







Obtenez l'exception stockée à l'intérieur de l' RuntimeExceptionobjet. La causevariable peut null

déterminer son type et la convertir en un type d'exception vérifié .


4. Attraper plusieurs exceptions

Les programmeurs détestent vraiment dupliquer le code. Ils ont même proposé un principe de développement correspondant : Ne vous répétez pas (DRY) . Mais lors de la gestion des exceptions, il arrive fréquemment qu'un trybloc soit suivi de plusieurs catchblocs avec le même code.

Ou il pourrait y avoir 3 catchblocs avec le même code et 2 autres catchblocs avec un autre code identique. Il s'agit d'une situation standard lorsque votre projet gère les exceptions de manière responsable.

À partir de la version 7, le langage Java a ajouté la possibilité de spécifier plusieurs types d'exceptions dans un seul catchbloc. Cela ressemble à ceci :

try
{
   // Code where an exception might occur
}
catch (ExceptionType1 | ExceptionType2 | ExceptionType3 name)
{
   // Exception handling code
}

Vous pouvez avoir autant catchde blocs que vous le souhaitez. Cependant, un seul catchbloc ne peut pas spécifier des exceptions qui héritent les unes des autres. En d'autres termes, vous ne pouvez pas écrire catch ( Exception| RuntimeExceptione), car la RuntimeExceptionclasse hérite de Exception.



5. Exceptions personnalisées

Vous pouvez toujours créer votre propre classe d'exception. Vous créez simplement une classe qui hérite de la RuntimeExceptionclasse. Cela ressemblera à ceci :

class ClassName extends RuntimeException
{
}

Nous discuterons des détails au fur et à mesure que vous apprendrez la POO, l'héritage, les constructeurs et le remplacement de méthode.

Cependant, même si vous n'avez qu'une classe simple comme celle-ci (entièrement sans code), vous pouvez toujours lancer des exceptions basées sur celle-ci :

Code Note
class Solution
{
   public static void main(String[] args)
   {
      throw new MyException();
   }
}

class MyException extends RuntimeException
{
}




Lancez un MyException.

Dans la quête Java Multithreading , nous approfondirons l'utilisation de nos propres exceptions personnalisées.