CodeGym /Java-Blog /Random-DE /Ausnahmen: Fangen und Hantieren
Autor
Oleksandr Miadelets
Head of Developers Team at CodeGym

Ausnahmen: Fangen und Hantieren

Veröffentlicht in der Gruppe Random-DE
Hallo! Ich hasse es, es zu erwähnen, aber ein großer Teil der Arbeit eines Programmierers besteht darin, sich mit Fehlern zu befassen. Meistens sein eigenes. Es stellt sich heraus, dass es keine Menschen gibt, die keine Fehler machen. Und solche Programme gibt es auch nicht. Bei der Behebung eines Fehlers kommt esAusnahmen: Fangen und Umgang – 1 natürlich vor allem darauf an, seine Ursache zu verstehen. Und viele Dinge können Fehler in einem Programm verursachen. Irgendwann fragten sich die Java-Entwickler, was mit den wahrscheinlichsten Programmierfehlern geschehen solle. Sie ganz zu vermeiden ist unrealistisch, denn Programmierer sind in der Lage, Dinge zu schreiben, die man sich nicht einmal vorstellen kann. :) Also müssen wir der Sprache einen Mechanismus zum Arbeiten mit Fehlern geben. Mit anderen Worten: Wenn in Ihrem Programm ein Fehler auftritt, benötigen Sie eine Art Skript für die nächsten Schritte. Was genau soll ein Programm tun, wenn ein Fehler auftritt? Heute werden wir diesen Mechanismus kennenlernen. Es heißt „ Ausnahmen in Java “.

Was ist eine Ausnahme?

Eine Ausnahme ist eine außergewöhnliche, ungeplante Situation, die während der Ausführung eines Programms auftritt. Es gibt viele Ausnahmen. Sie haben beispielsweise Code geschrieben, der Text aus einer Datei liest und die erste Zeile anzeigt.

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);
   }
}
Was aber, wenn es keine solche Datei gibt! Das Programm generiert eine Ausnahme: FileNotFoundException. Ausgabe: Ausnahme im Thread „main“ java.io.FileNotFoundException: C:\Benutzer\Benutzername\Desktop\test.txt (Das System kann den angegebenen Pfad nicht finden) In Java wird jede Ausnahme durch eine separate Klasse dargestellt. Alle diese Ausnahmeklassen leiten sich von einem gemeinsamen „Vorfahren“ ab – der Throwableübergeordneten Klasse. Der Name einer Ausnahmeklasse gibt normalerweise genau an, warum die Ausnahme aufgetreten ist:
  • FileNotFoundException(die Datei wurde nicht gefunden)

  • ArithmeticException(Beim Ausführen einer mathematischen Operation ist eine Ausnahme aufgetreten.)

  • ArrayIndexOutOfBoundsException(Der Index liegt außerhalb der Grenzen des Arrays). Diese Ausnahme tritt beispielsweise auf, wenn Sie versuchen, Position 23 eines Arrays anzuzeigen, das nur 10 Elemente enthält.
Insgesamt gibt es in Java fast 400 solcher Klassen! Warum so viele? Um die Arbeit für Programmierer komfortabler zu gestalten. Stellen Sie sich Folgendes vor: Sie schreiben ein Programm, und während es ausgeführt wird, generiert es eine Ausnahme, die so aussieht:

Exception in thread "main"
Uhhhh. :/ Das hilft nicht viel. Es ist nicht klar, was der Fehler bedeutet oder woher er kommt. Hier gibt es keine hilfreichen Informationen. Aber die große Vielfalt an Ausnahmeklassen in Java gibt dem Programmierer das Wichtigste: die Art des Fehlers und seine wahrscheinliche Ursache (eingebettet im Klassennamen). Es ist etwas ganz anderes zu sehen

Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (The system cannot find the specified path)
Es ist sofort klar, wo das Problem liegen könnte und wo man mit der Suche beginnen muss, um das Problem zu lösen! Ausnahmen sind wie Instanzen beliebiger Klassen Objekte.

Ausnahmen abfangen und behandeln

Java verfügt über spezielle Codeblöcke für die Arbeit mit Ausnahmen: try, catchund finally. Ausnahmen: Fangen und Umgang – 2 Code, bei dem der Programmierer glaubt, dass eine Ausnahme auftreten könnte, wird in den Block eingefügt try. Das bedeutet nicht, dass hier eine Ausnahme auftritt. Das bedeutet, dass es hier auftreten könnte und der Programmierer sich dieser Möglichkeit bewusst ist. Der von Ihnen erwartete Fehlertyp wird in den catchBlock eingefügt. Darin ist auch der gesamte Code enthalten, der im Ausnahmefall ausgeführt werden soll. Hier ist ein Beispiel:

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!");
   }
}
Ausgabe: Fehler! Datei nicht gefunden! Wir gliedern unseren Code in zwei Blöcke. Im ersten Block gehen wir davon aus, dass der Fehler „Datei nicht gefunden“ auftreten kann. Das ist der tryBlock. Im zweiten Schritt teilen wir dem Programm mit, was zu tun ist, wenn ein Fehler auftritt. Und der spezifische Fehlertyp: FileNotFoundException. Wenn wir eine andere Ausnahmeklasse in die Klammern des catchBlocks setzen, FileNotFoundExceptionwird diese nicht abgefangen.

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!");
   }
}
Ausgabe: Ausnahme im Thread „main“ java.io.FileNotFoundException: C:\Benutzer\Benutzername\Desktop\test.txt (Das System kann den angegebenen Pfad nicht finden) Der Code im catchBlock wurde nicht ausgeführt, weil wir „konfiguriert“ haben Dieser Block soll „catch“ sein ArithmeticException, und der Code im tryBlock hat einen anderen Typ ausgelöst: FileNotFoundException. Wir haben keinen Code für die Verarbeitung geschrieben FileNotFoundException, daher zeigt das Programm die Standardinformationen für an FileNotFoundException. Hier müssen Sie auf drei Dinge achten. Nummer Eins. Sobald in einer Zeile des tryBlocks eine Ausnahme auftritt, wird der folgende Code nicht ausgeführt. Bei der Ausführung des Programms wird sofort zum catchBlock „gesprungen“. Zum Beispiel:

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!");
   }
}
Ausgabe: Division durch Null Das Programm ist zum Catch-Block gesprungen! Fehler! Man kann nicht durch Null dividieren! In der zweiten Zeile des tryBlocks versuchen wir, durch 0 zu dividieren, was zu einem führt ArithmeticException. Folglich werden die Zeilen 3-9 des tryBlocks nicht ausgeführt. Wie gesagt, das Programm beginnt sofort mit der Ausführung des catchBlocks. Nummer zwei. Es können mehrere catchBlöcke vorhanden sein. Wenn der Code im tryBlock möglicherweise nicht nur eine, sondern mehrere verschiedene Arten von Ausnahmen auslöst, können Sie catchfür jede davon einen Block schreiben.

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 diesem Beispiel haben wir zwei catchBlöcke geschrieben. Wenn FileNotFoundExceptionim Block ein Fehler auftritt , wird tryder erste Block ausgeführt. catchTritt ein ArithmeticExceptionFehler auf, wird der zweite Block ausgeführt. Sie könnten 50 catchBlöcke schreiben, wenn Sie wollten. Natürlich ist es besser, keinen Code zu schreiben, der 50 verschiedene Arten von Ausnahmen auslösen könnte. :) Drittens. Woher wissen Sie, welche Ausnahmen Ihr Code auslösen könnte? Nun, einige davon können Sie vielleicht erraten, aber es ist Ihnen unmöglich, alles im Kopf zu behalten. Der Java-Compiler kennt daher die häufigsten Ausnahmen und die Situationen, in denen sie auftreten können. Wenn Sie beispielsweise Code schreiben, von dem der Compiler weiß, dass er zwei Arten von Ausnahmen auslösen könnte, wird Ihr Code erst kompiliert, wenn Sie diese behandelt haben. Wir werden unten Beispiele dafür sehen. Nun ein paar Worte zur Ausnahmebehandlung. Es gibt zwei Möglichkeiten, Ausnahmen zu behandeln. Das erste haben wir bereits kennengelernt: Die Methode kann die Ausnahme selbst in einem catch()Block behandeln. Es gibt eine zweite Option: Die Methode kann die Ausnahme erneut im Aufrufstapel auslösen. Was bedeutet das? Wir haben zum Beispiel eine Klasse mit derselben printFirstString()Methode, die eine Datei liest und deren erste Zeile anzeigt:

public static void printFirstString(String filePath) {

   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
Derzeit lässt sich unser Code nicht kompilieren, da er unbehandelte Ausnahmen aufweist. In Zeile 1 geben Sie den Pfad zur Datei an. Der Compiler weiß, dass ein solcher Code leicht eine FileNotFoundException. In Zeile 3 lesen Sie den Text aus der Datei. IOExceptionDieser Vorgang könnte leicht zu einem (Eingabe-/Ausgabefehler) führen . Jetzt sagt der Compiler zu dir: „Alter, ich werde diesen Code nicht genehmigen und ihn nicht kompilieren, bis du mir sagst, was ich tun soll, wenn eine dieser Ausnahmen auftritt. Und basierend auf dem Code, den du geschrieben hast, könnten sie sicherlich passieren.“ !" Daran führt kein Weg vorbei: Man muss mit beidem klarkommen! Die erste Methode zur Ausnahmebehandlung kennen wir bereits: Wir müssen unseren Code in einen tryBlock einfügen und zwei catchBlöcke hinzufügen:

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();
   }
}
Aber das ist nicht die einzige Option. Wir könnten die Ausnahme einfach nach oben werfen, anstatt Fehlerbehandlungscode in die Methode zu schreiben. Dies geschieht über das Schlüsselwort throwsin der Methodendeklaration:

public static void printFirstString(String filePath) throws FileNotFoundException, IOException {
   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
Nach dem Schlüsselwort throwsgeben wir eine durch Kommas getrennte Liste aller Arten von Ausnahmen an, die die Methode möglicherweise auslöst. Warum? Wenn nun jemand die Methode im Programm aufrufen möchte printFirstString(), muss er oder sie (nicht Sie) die Ausnahmebehandlung implementieren. Angenommen, einer Ihrer Kollegen hat an anderer Stelle im Programm eine Methode geschrieben, die Ihre printFirstString()Methode aufruft:

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");
}
Wir erhalten eine Fehlermeldung! Dieser Code lässt sich nicht kompilieren! Wir haben in der Methode keinen Ausnahmebehandlungscode geschrieben printFirstString(). Dadurch liegt diese Aufgabe nun auf den Schultern derjenigen, die die Methode anwenden. Mit anderen Worten: Die methodWrittenByYourColleague()Methode verfügt jetzt über dieselben zwei Optionen: Sie muss entweder einen try-catchBlock verwenden, um beide Ausnahmen zu behandeln, oder sie muss sie erneut auslösen.

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");
}
Im zweiten Fall muss die nächste Methode im Aufrufstapel – diejenige, die aufruft methodWrittenByYourColleague()– die Ausnahmen behandeln. Aus diesem Grund nennen wir dies „Auslösen oder Weitergeben der Ausnahme“. Wenn Sie mit dem Schlüsselwort Ausnahmen nach oben auslösen throws, wird Ihr Code kompiliert. An diesem Punkt scheint der Compiler zu sagen: „Okay, okay. Ihr Code enthält eine Reihe potenzieller Ausnahmen, aber ich werde ihn kompilieren. Aber wir werden auf dieses Gespräch zurückkommen!“ Und wenn Sie eine Methode aufrufen, die nicht behandelte Ausnahmen hat, erfüllt der Compiler sein Versprechen und erinnert Sie erneut daran. Abschließend sprechen wir über den finallyBlock (Entschuldigung für das Wortspiel). Dies ist der letzte Teil des try-catch-finallyAusnahmebehandlungs-Triumvirats..

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 diesem Beispiel wird der Code innerhalb des Blocks in beiden Fällen ausgeführt. Wenn der Code im tryBlock vollständig ausgeführt wird, ohne Ausnahmen auszulösen, finallywird der Block am Ende ausgeführt. Wenn der Code innerhalb des tryBlocks durch eine Ausnahme unterbrochen wird und das Programm zu dem Block springt catch, finallywird der Block weiterhin nach dem Code innerhalb des catchBlocks ausgeführt. Warum ist das notwendig? Sein Hauptzweck besteht darin, obligatorischen Code auszuführen: Code, der unabhängig von den Umständen ausgeführt werden muss. Beispielsweise werden dadurch häufig einige vom Programm verwendete Ressourcen freigegeben. In unserem Code öffnen wir einen Stream, um Informationen aus der Datei zu lesen und an das Objekt zu übergeben BufferedReader. Wir müssen unseren Reader schließen und die Ressourcen freigeben. Dies muss auf jeden Fall erfolgen – wenn das Programm ordnungsgemäß funktioniert und eine Ausnahme auslöst. Der finallyBlock ist ein sehr praktischer Ort, um dies zu tun:

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();
       }
   }
}
Jetzt sind wir sicher, dass wir die Ressourcen schonen, unabhängig davon, was passiert, wenn das Programm läuft. :) Das ist nicht alles, was Sie über Ausnahmen wissen müssen. Fehlerbehandlung ist ein super wichtiges Thema in der Programmierung. Viele Artikel sind diesem Thema gewidmet. In der nächsten Lektion erfahren wir, welche Arten von Ausnahmen es gibt und wie Sie eigene Ausnahmen erstellen. :) Bis dann!
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION