JavaRush /Blog Java /Random-FR /Exceptions en Java : capture et gestion

Exceptions en Java : capture et gestion

Publié dans le groupe Random-FR
Bonjour! Je déteste vous le dire, mais une grande partie du travail d'un programmeur consiste à gérer les erreurs. Et le plus souvent - avec les leurs. Il se trouve qu’il n’y a personne qui ne commette pas d’erreurs. Et de tels programmes n'existent pas non plus. Bien entendu, l’essentiel lorsqu’on travaille sur une erreur est d’en comprendre la cause. Et il peut y avoir tout un tas de raisons à cela dans le programme. À un moment donné, les créateurs de Java ont été confrontés à une question : que faire de ces erreurs très potentielles dans les programmes ? Les éviter complètement n’est pas réaliste. Les programmeurs peuvent écrire quelque chose qui est impossible à imaginer :) Cela signifie qu'il est nécessaire d'intégrer dans le langage un mécanisme pour traiter les erreurs. En d'autres termes, si une erreur s'est produite dans le programme, un script est nécessaire pour poursuivre le travail. Que doit faire exactement le programme lorsqu’une erreur se produit ? Aujourd'hui, nous allons nous familiariser avec ce mécanisme. Et cela s'appelle « Exceptions » .

Qu'est-ce qu'une exception en Java

Une exception est une situation exceptionnelle et imprévue survenue pendant le fonctionnement du programme. Il peut y avoir de nombreux exemples d’exceptions en Java. Par exemple, vous avez écrit un code qui lit le texte d'un fichier et affiche la première ligne sur la console.
public class Main {

   public static void main(String[] args) throws IOException {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
       String firstString = reader.readLine();
       System.out.println(firstString);
   }
}
Mais un tel fichier n’existe pas ! Le résultat du programme sera une exception - FileNotFoundException. Conclusion:

Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Системе не удается найти указанный путь)
Chaque exception est représentée par une classe distincte en Java. Toutes les classes d'exception proviennent d'un « ancêtre » commun : la classe parent Throwable. Le nom de la classe d'exception reflète généralement brièvement la raison de son apparition :
  • FileNotFoundException(fichier introuvable)
  • ArithmeticException(exception lors de l'exécution d'une opération mathématique)
  • ArrayIndexOutOfBoundsException(le numéro de la cellule du tableau est précisé au-delà de sa longueur). Par exemple, si vous essayez d'afficher un tableau de cellules [23] sur la console pour un tableau de longueur 10.
Il existe près de 400 classes de ce type en Java ! Pourquoi tant ? Précisément pour permettre aux programmeurs de travailler plus facilement avec eux. Imaginez : vous avez écrit un programme, et lorsqu'il s'exécute, il lève une exception qui ressemble à ceci :
Exception in thread "main"
Euh-euh :/ Rien n'est clair. De quel type d’erreur il s’agit et d’où elle vient n’est pas clair. Il n'y a aucune information utile. Mais grâce à une telle variété de classes, le programmeur obtient l'essentiel pour lui-même - le type d'erreur et sa cause probable, qui est contenue dans le nom de la classe. Après tout, c’est une chose complètement différente à voir dans la console :
Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Системе не удается найти указанный путь)
Il devient immédiatement clair quel pourrait être le problème et « dans quelle direction creuser » pour résoudre le problème ! Les exceptions, comme toutes les instances de classes, sont des objets.

Détecter et gérer les exceptions

Pour travailler avec des exceptions en Java, il existe des blocs de code spéciaux : try, catchet finally. Exceptions : interception et traitement - 2Le code dans lequel le programmeur s'attend à ce que des exceptions se produisent est placé dans un bloc try. Cela ne signifie pas qu’une exception se produira nécessairement à cet endroit. Cela signifie que cela peut arriver là-bas et que le programmeur en est conscient. Le type d’erreur que vous vous attendez à recevoir est placé dans un bloc catch(« catch »). C'est également là que est placé tout le code qui doit être exécuté si une exception se produit. Voici un exemple :
public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {

       System.out.println("Error! File not found!");
   }
}
Conclusion:

Ошибка! Файл не найден!
Nous mettons notre code en deux blocs. Dans le premier bloc, nous nous attendons à ce qu'une erreur « Fichier non trouvé » puisse se produire. C'est un bloc try. Dans la seconde, nous indiquons au programme quoi faire si une erreur se produit. De plus, il existe un type spécifique d'erreur - FileNotFoundException. Si nous passons catchune autre classe d’exception entre crochets de bloc, elle ne sera pas interceptée.
public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (ArithmeticException e) {

       System.out.println("Error! File not found!");
   }
}
Conclusion:

Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Системе не удается найти указанный путь)
Le code du bloc catchn'a pas fonctionné car nous avons « configuré » ce bloc pour intercepter ArithmeticException, et le code du bloc trya rejeté un autre type - FileNotFoundException. Nous n'avons pas écrit de script pour FileNotFoundException, le programme a donc affiché dans la console les informations affichées par défaut pour FileNotFoundException. Ici, vous devez faire attention à 3 choses. D'abord. Dès qu'une exception se produit dans une ligne de code d'un bloc try, le code qui la suit ne sera plus exécuté. L'exécution du programme « sautera » immédiatement au bloc catch. Par exemple:
public static void main(String[] args) {
   try {
       System.out.println("Divide a number by zero");
       System.out.println(366/0);//this line of code will throw an exception

       System.out.println("This");
       System.out.println("code");
       System.out.println("Not");
       System.out.println("will");
       System.out.println("done!");

   } catch (ArithmeticException e) {

       System.out.println("The program jumped to the catch block!");
       System.out.println("Error! You can't divide by zero!");
   }
}
Conclusion:

Делим число на ноль 
Программа перепрыгнула в блок catch! 
Ошибка! Нельзя делить на ноль! 
Dans le bloc tryde la deuxième ligne, nous avons essayé de diviser un nombre par 0, ce qui a entraîné une exception ArithmeticException. Après cela, les lignes 6 à 10 du bloc tryne seront plus exécutées. Comme nous l'avons dit, le programme a immédiatement commencé à exécuter le bloc catch. Deuxième. Il peut y avoir plusieurs blocs catch. Si le code d'un bloc trypeut générer non pas un, mais plusieurs types d'exceptions, vous pouvez écrire votre propre bloc pour chacune d'elles catch.
public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       System.out.println(366/0);
       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {

       System.out.println("Error! File not found!");

   } catch (ArithmeticException e) {

       System.out.println("Error! Division by 0!");

   }
}
Dans cet exemple, nous avons écrit deux blocs catch. Si , tryse produit dans le bloc FileNotFoundException, le premier bloc sera exécuté catch. Si cela se produit ArithmeticException, le second sera exécuté. Vous pouvez écrire au moins 50 blocs catch... Mais, bien sûr, il vaut mieux ne pas écrire de code qui peut générer 50 types d'erreurs différents :) Troisièmement. Comment savoir quelles exceptions votre code peut générer ? Eh bien, vous pouvez bien sûr en deviner certains, mais il est impossible de tout garder en tête. Par conséquent, le compilateur Java connaît les exceptions les plus courantes et sait dans quelles situations elles peuvent se produire. Par exemple, si vous avez écrit du code et que le compilateur sait que 2 types d'exceptions peuvent survenir lors de son fonctionnement, votre code ne sera pas compilé tant que vous ne les aurez pas gérés. Nous en verrons des exemples ci-dessous. Maintenant concernant la gestion des exceptions. Il existe 2 façons de les traiter. Nous avons déjà rencontré le premier : la méthode peut gérer l'exception indépendamment dans le bloc catch(). Il existe une deuxième option : la méthode peut lever une exception dans la pile des appels. Qu'est-ce que ça veut dire? Par exemple, dans notre classe nous avons une méthode - la même printFirstString()- qui lit un fichier et affiche sa première ligne à la console :
public static void printFirstString(String filePath) {

   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
Actuellement, notre code ne se compile pas car il comporte des exceptions non gérées. Sur la ligne 1 vous indiquez le chemin d'accès au fichier. Le compilateur sait qu'un tel code peut facilement conduire à FileNotFoundException. À la ligne 3, vous lisez le texte du fichier. IOExceptionDans ce processus , une erreur lors des entrées-sorties (Input-Output) peut facilement se produire . Maintenant, le compilateur vous dit : « Mec, je n'approuverai pas ce code ni ne le compilerai jusqu'à ce que tu me dises ce que je dois faire si l'une de ces exceptions se produit. Et cela peut certainement se produire en fonction du code que vous avez écrit ! » . Il n'y a nulle part où aller, vous devez traiter les deux ! La première option de traitement nous est déjà familière : nous devons placer notre code dans un bloc tryet ajouter deux blocs catch:
public static void printFirstString(String filePath) {

   try {
       BufferedReader reader = new BufferedReader(new FileReader(filePath));
       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
       System.out.println("Error, file not found!");
       e.printStackTrace();
   } catch (IOException e) {
       System.out.println("Error while inputting/outputting data from file!");
       e.printStackTrace();
   }
}
Mais ce n’est pas la seule option. Nous pouvons éviter d'écrire un script pour l'erreur à l'intérieur de la méthode et simplement lancer l'exception en haut. Cela se fait à l'aide du mot-clé throws, qui est écrit dans la déclaration de la méthode :
public static void printFirstString(String filePath) throws FileNotFoundException, IOException {
   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
Après le mot, throwsnous listons, séparés par des virgules, tous les types d'exceptions que cette méthode peut lever pendant le fonctionnement. Pourquoi cela est-il fait ? Désormais, si quelqu'un dans le programme souhaite appeler la méthode printFirstString(), il devra implémenter lui-même la gestion des exceptions. Par exemple, dans une autre partie du programme, un de vos collègues a écrit une méthode au sein de laquelle il appelle votre méthode printFirstString():
public static void yourColleagueMethod() {

   //...your colleague's method does something

   //...and at one moment calls your printFirstString() method with the file it needs
   printFirstString("C:\\Users\\Eugene\\Desktop\\testFile.txt");
}
Erreur, le code ne compile pas ! printFirstString()Nous n'avons pas écrit de script de gestion des erreurs dans la méthode . Par conséquent, la tâche incombe à ceux qui utiliseront cette méthode. Autrement dit, la méthode yourColleagueMethod()est désormais confrontée aux mêmes 2 options : elle doit soit traiter les deux exceptions qui lui « arrivent » en utilisant try-catch, soit les transmettre davantage.
public static void yourColleagueMethod() throws FileNotFoundException, IOException {
   //...the method does something

   //...and at one moment calls your printFirstString() method with the file it needs
   printFirstString("C:\\Users\\Eugene\\Desktop\\testFile.txt");
}
Dans le second cas, le traitement incombera à la méthode suivante sur la pile - celle qui appellera yourColleagueMethod(). C'est pourquoi un tel mécanisme est appelé « lancer une exception vers le haut » ou « passer vers le haut ». Lorsque vous lancez des exceptions en utilisant throws, le code se compile. À ce moment-là, le compilateur semble dire : « D'accord, d'accord. Votre code contient de nombreuses exceptions potentielles, mais je vais quand même le compiler. Nous reviendrons sur cette conversation ! Et lorsque vous appelez une méthode quelque part dans le programme qui n'a pas géré ses exceptions, le compilateur tient sa promesse et vous les rappelle à nouveau. Enfin, nous parlerons du blocage finally(pardonnez le jeu de mots). Il s'agit de la dernière partie du triumvirat de gestion des exceptions try-catch-finally. Sa particularité est qu'il est exécuté dans n'importe quel scénario de fonctionnement du programme.
public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
       System.out.println("Error! File not found!");
       e.printStackTrace();
   } finally {
       System.out.println("And here is the finally block!");
   }
}
Dans cet exemple, le code à l’intérieur du bloc finallyest exécuté dans les deux cas. Si le code du bloc tryest exécuté entièrement et ne lève pas d'exception, le bloc se déclenchera à la fin finally. Si le code à l'intérieur tryest interrompu et que le programme saute au bloc catch, une fois le code à l'intérieur exécuté catch, le bloc sera toujours sélectionné finally. Pourquoi est-ce nécessaire ? Son objectif principal est d'exécuter la partie requise du code ; cette partie qui doit être complétée quelles que soient les circonstances. Par exemple, cela libère souvent certaines ressources utilisées par le programme. Dans notre code, nous ouvrons un flux pour lire les informations d'un fichier et les transmettons à un fichier BufferedReader. Le nôtre readerdoit être fermé et les ressources libérées. Cela doit être fait dans tous les cas : peu importe que le programme fonctionne comme prévu ou qu'il lève une exception. Il est pratique de faire cela en bloc finally:
public static void main(String[] args) throws IOException {

   BufferedReader reader = null;
   try {
       reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
       e.printStackTrace();
   } finally {
       System.out.println("And here is the finally block!");
       if (reader != null) {
           reader.close();
       }
   }
}
Maintenant, nous sommes absolument sûrs d'avoir pris soin des ressources occupées, quoi qu'il arrive pendant l'exécution du programme :) Ce n'est pas tout ce que vous devez savoir sur les exceptions. La gestion des erreurs est un sujet très important en programmation : plus d'un article y est consacré. Dans la prochaine leçon, nous apprendrons quels types d'exceptions existent et comment créer votre propre exception :) À bientôt !
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION