CodeGym /Java Blog /Willekeurig /Uitzonderingen: vangen en hanteren
John Squirrels
Niveau 41
San Francisco

Uitzonderingen: vangen en hanteren

Gepubliceerd in de groep Willekeurig
Hoi! Ik zeg het niet graag, maar een groot deel van het werk van een programmeur bestaat uit het omgaan met fouten. Meestal zijn of haar eigen. Het blijkt dat er geen mensen zijn die geen fouten maken. En zulke programma's zijn er ook niet. Uitzonderingen: vangen en hanteren - 1 Bij het oplossen van een fout is het natuurlijk het belangrijkste om de oorzaak ervan te begrijpen. En veel dingen kunnen bugs in een programma veroorzaken. Op een gegeven moment vroegen de makers van Java zich af wat ze moesten doen met de meest waarschijnlijke programmeerfouten. Ze volledig vermijden is niet realistisch, programmeurs zijn in staat dingen te schrijven die je je niet eens kunt voorstellen. :) We moeten de taal dus een mechanisme geven om met fouten te werken. Met andere woorden, als er een fout in je programma zit, heb je een soort script nodig voor wat je vervolgens moet doen. Wat moet een programma precies doen als er een fout optreedt? Vandaag zullen we kennis maken met dit mechanisme. Het heet " uitzonderingen in Java ".

Wat is een uitzondering?

Een uitzondering is een uitzonderlijke, ongeplande situatie die optreedt terwijl een programma wordt uitgevoerd. Er zijn veel uitzonderingen. U hebt bijvoorbeeld code geschreven die tekst uit een bestand leest en de eerste regel weergeeft.

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);
   }
}
Maar wat als er geen dergelijk bestand is! Het programma genereert een uitzondering: FileNotFoundException. Uitvoer: Uitzondering in thread "main" java.io.FileNotFoundException: C:\Users\Gebruikersnaam\Desktop\test.txt (Het systeem kan het opgegeven pad niet vinden) In Java wordt elke uitzondering vertegenwoordigd door een afzonderlijke klasse. Al deze uitzonderingsklassen zijn afgeleid van een gemeenschappelijke "voorouder": de Throwablebovenliggende klasse. De naam van een uitzonderingsklasse geeft meestal beknopt weer waarom de uitzondering is opgetreden:
  • FileNotFoundException(het bestand is niet gevonden)

  • ArithmeticException(er is een uitzondering opgetreden tijdens het uitvoeren van een wiskundige bewerking)

  • ArrayIndexOutOfBoundsException(de index valt buiten de grenzen van de array). Deze uitzondering doet zich bijvoorbeeld voor als u positie 23 probeert weer te geven van een matrix met slechts 10 elementen.
In totaal heeft Java bijna 400 van dergelijke klassen! Waarom zo veel? Om ze handiger te maken voor programmeurs om mee te werken. Stel je dit eens voor: je schrijft een programma en terwijl het wordt uitgevoerd, genereert het een uitzondering die er als volgt uitziet:

Exception in thread "main"
Uhhh. :/ Dat helpt niet veel. Het is niet duidelijk wat de fout betekent of waar deze vandaan komt. Er is hier geen nuttige informatie. Maar de grote verscheidenheid aan uitzonderingsklassen in Java geeft de programmeur het belangrijkste: het type fout en de waarschijnlijke oorzaak (ingebed in de klassenaam). Het is iets heel anders om te zien

Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (The system cannot find the specified path)
Het is meteen duidelijk wat het probleem zou kunnen zijn en waar te beginnen met graven om het probleem op te lossen! Uitzonderingen, zoals instanties van alle klassen, zijn objecten.

Uitzonderingen opvangen en afhandelen

Java heeft speciale codeblokken voor het werken met uitzonderingen: try, catchen finally. Uitzonderingen: vangen en hanteren - 2 Code waarvan de programmeur denkt dat er een uitzondering kan optreden, wordt in het blok geplaatst try. Dat betekent niet dat hier een uitzondering zal optreden. Het betekent dat het hier kan voorkomen en de programmeur is op de hoogte van deze mogelijkheid. Het type fout dat u verwacht, wordt in het catchblok geplaatst. Dit bevat ook alle code die moet worden uitgevoerd als er een uitzondering optreedt. Hier is een voorbeeld:

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!");
   }
}
Uitvoer: fout! Bestand niet gevonden! We stoppen onze code in twee blokken. In het eerste blok verwachten we dat de foutmelding "Bestand niet gevonden" kan optreden. Dit is het tryblok. In de tweede vertellen we het programma wat het moet doen als er een fout optreedt. En het specifieke fouttype: FileNotFoundException. Als we een andere uitzonderingsklasse tussen haakjes van het catchblok plaatsen, FileNotFoundExceptionwordt deze niet opgevangen.

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!");
   }
}
Uitvoer: Uitzondering in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Het systeem kan het opgegeven pad niet vinden) De code in het catchblok is niet uitgevoerd, omdat we "geconfigureerd" dit blok om te vangen ArithmeticException, en de code in het tryblok gooide een ander type: FileNotFoundException. We hebben geen code geschreven om te verwerken FileNotFoundException, dus het programma geeft de standaardinformatie weer voor FileNotFoundException. Hier moet je op drie dingen letten. Nummer een. Zodra er een uitzondering optreedt op een regel in het tryblok, wordt de volgende code niet uitgevoerd. De uitvoering van het programma "springt" onmiddellijk naar het catchblok. Bijvoorbeeld:

public static void main(String[] args) {
   try {
       System.out.println("Divide 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("will not");
       System.out.println("be");
       System.out.println("executed!");

   } catch (ArithmeticException e) {

       System.out.println("The program jumped to the catch block!");
       System.out.println("Error! You can't divide by zero!");
   }
}
Uitvoer: Delen door nul Het programma is naar het catch-blok gesprongen! Fout! Je kunt niet delen door nul! Op de tweede regel van het tryblok proberen we te delen door 0, wat resulteert in een ArithmeticException. Bijgevolg tryworden de regels 3-9 van het blok niet uitgevoerd. Zoals we al zeiden, begint het programma onmiddellijk met het uitvoeren van het catchblok. Nummer twee. Er kunnen meerdere catchblokken zijn. Als de code in het tryblok niet één, maar verschillende typen uitzonderingen genereert, kunt u catchvoor elk daarvan een blok schrijven.

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!");
      
   }
}
In dit voorbeeld hebben we twee catchblokken geschreven. Als er een FileNotFoundExceptionvoorkomt in het blok, wordt tryhet eerste blok uitgevoerd. catchAls er een ArithmeticExceptionoptreedt, wordt het tweede blok uitgevoerd. Je zou 50 catchblokken kunnen schrijven als je dat zou willen. Het is natuurlijk beter om geen code te schrijven die 50 verschillende soorten uitzonderingen kan opleveren. :) Ten derde. Hoe weet u welke uitzonderingen uw code kan genereren? Nou, je kunt er misschien een paar raden, maar het is voor jou onmogelijk om alles in je hoofd te houden. De Java-compiler kent dus de meest voorkomende uitzonderingen en de situaties waarin deze zich kunnen voordoen. Als u bijvoorbeeld code schrijft waarvan de compiler weet dat deze twee typen uitzonderingen kan genereren, wordt uw code pas gecompileerd als u ze afhandelt. We zullen voorbeelden hiervan hieronder zien. Nu enkele woorden over het afhandelen van uitzonderingen. Er zijn 2 manieren om met uitzonderingen om te gaan. De eerste zijn we al tegengekomen: de methode kan de uitzondering zelf in een catch()blok afhandelen. Er is een tweede optie: de methode kan de exception opnieuw op de call-stack gooien. Wat betekent dat? We hebben bijvoorbeeld een klasse met dezelfde printFirstString()methode, die een bestand leest en de eerste regel weergeeft:

public static void printFirstString(String filePath) {

   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
Op dit moment kan onze code niet worden gecompileerd, omdat deze onverwerkte uitzonderingen heeft. In regel 1 geeft u het pad naar het bestand op. De compiler weet dat dergelijke code gemakkelijk een FileNotFoundException. In regel 3 lees je de tekst uit het bestand. Dit proces kan gemakkelijk resulteren in een IOException(invoer-/uitvoerfout). Nu zegt de compiler tegen je: "Kerel, ik keur deze code niet goed en ik zal hem niet compileren totdat je me vertelt wat ik moet doen als een van deze uitzonderingen zich voordoet. En ze kunnen zeker gebeuren op basis van de code die je hebt geschreven !" Je kunt er niet omheen: je moet beide aanpakken! We kennen de eerste methode voor het afhandelen van uitzonderingen al: we moeten onze code in een tryblok plaatsen en twee catchblokken toevoegen:

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("File input/output error!");
       e.printStackTrace();
   }
}
Maar dit is niet de enige optie. We kunnen de uitzondering gewoon hoger gooien in plaats van foutafhandelingscode in de methode te schrijven. Dit gebeurt met behulp van het trefwoord throwsin de methodedeclaratie:

public static void printFirstString(String filePath) throws FileNotFoundException, IOException {
   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
Na het trefwoord throwsgeven we een door komma's gescheiden lijst aan van alle soorten uitzonderingen die de methode kan opleveren. Waarom? Als iemand nu de methode in het programma wil aanroepen printFirstString(), zal hij of zij (niet jij) exception handling moeten implementeren. Stel bijvoorbeeld dat een van uw collega's elders in het programma een methode heeft geschreven die uw printFirstString()methode aanroept:

public static void yourColleagueMethod() {

   // Your colleague's method does something

   //...and then calls your printFirstString() method with the file it needs
   printFirstString("C:\\Users\\Henry\\Desktop\\testFile.txt");
}
We krijgen een foutmelding! Deze code compileert niet! We hebben geen code voor het afhandelen van uitzonderingen in de printFirstString()methode geschreven. Hierdoor valt deze taak nu op de schouders van degenen die de methode gebruiken. Met andere woorden, de methodWrittenByYourColleague()methode heeft nu dezelfde 2 opties: hij moet ofwel een try-catchblok gebruiken om beide uitzonderingen af ​​te handelen, of ze opnieuw gooien.

public static void yourColleagueMethod() throws FileNotFoundException, IOException {
   // The method does something

   //...and then calls your printFirstString() method with the file it needs
   printFirstString("C:\\Users\\Henry\\Desktop\\testFile.txt");
}
In het tweede geval zal de volgende methode in de call-stack - degene die aanroept methodWrittenByYourColleague()- de uitzonderingen moeten afhandelen. Daarom noemen we dit "het gooien of doorgeven van de uitzondering". Als u uitzonderingen naar boven gooit met het trefwoord throws, wordt uw code gecompileerd. Op dit punt lijkt de compiler te zeggen: "Oké, oké. Je code bevat een aantal mogelijke uitzonderingen, maar ik zal het compileren. Maar we komen terug op dit gesprek!" En wanneer u een methode aanroept die onverwerkte uitzonderingen heeft, komt de compiler zijn belofte na en herinnert u er opnieuw aan. Ten slotte zullen we het hebben over het finallyblok (sorry voor de woordspeling). Dit is het laatste deel van het try-catch-finallydriemanschap voor het afhandelen van uitzonderingen..

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's the finally block!");
   }
}
finallyIn dit voorbeeld wordt de code in het blok in beide gevallen uitgevoerd. Als de code in het tryblok volledig wordt uitgevoerd zonder uitzonderingen te genereren, finallywordt het blok uiteindelijk uitgevoerd. Als de code in het tryblok wordt onderbroken door een uitzondering en het programma naar het catchblok springt, finallywordt het blok nog steeds achter de code in het catchblok uitgevoerd. Waarom is dit nodig? Het belangrijkste doel is het uitvoeren van verplichte code: code die ongeacht de omstandigheden moet worden uitgevoerd. Het maakt bijvoorbeeld vaak bepaalde bronnen vrij die door het programma worden gebruikt. In onze code openen we een stream om informatie uit het bestand te lezen en door te geven aan het BufferedReaderobject. We moeten onze reader sluiten en de bronnen vrijgeven. Dit moet hoe dan ook worden gedaan - wanneer het programma werkt zoals het hoort en wanneer het een uitzondering genereert. Het finallyblok is een erg handige plek om dit te doen:

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's the finally block!");
       if (reader != null) {
           reader.close();
       }
   }
}
Nu weten we zeker dat we voor de bronnen zullen zorgen, ongeacht wat er gebeurt als het programma draait. :) Dat is niet alles wat u moet weten over uitzonderingen. Foutafhandeling is een super belangrijk onderwerp bij het programmeren. Er zijn tal van artikelen aan gewijd. In de volgende les zullen we zien welke typen uitzonderingen er zijn en hoe u uw eigen uitzonderingen kunt maken. :) Zie je dan!
Opmerkingen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION