Fehlerbehandlung
| Website: | Hamburg Open Online University | 
| Kurs: | Programmieren mit Java | 
| Buch: | Fehlerbehandlung | 
| Gedruckt von: | Gast | 
| Datum: | Dienstag, 4. November 2025, 06:55 | 
Beschreibung
Hier lernst Du, wie du mit Fehlern bei der Programmierung umgehst.
Gundlagen
Beim Programmieren und beim Ausführen eines Programms können die
        verschiedensten Fehler auftreten. Einige dieser Fehler können bereits
        während des Programmierens von der IDE erkannt werden. Dies sind zum
        Beispiel Syntaxfehler oder ein Aufruf von einer Methode, die nicht
        deklariert wurde. Fehler, die die Entwicklungsumgebung nicht erkennen
        kann, sind beispielsweise Fehler in der Programmlogik, also wenn der
        Programmierer schicht das falsche Programm schreibt. Die dritte Klasse
        von Fehlern sind die Laufzeitfehler, also Fehler, die erst auftreten
        während das Programm ausgeführt wird. Ein Laufzeitfehler wäre zum
        Beispiel, wenn man versucht in eine Datei zu schreiben, für die das
        Programm keine Schreibberechtigung hat.
    Jeder hat
        schon einmal einen nicht behandelten Laufzeitfehler erlebt. Dies äußert
        sich dadurch, dass das Programm einfach abstürzt. Damit Java-Programme
        nicht abstürzen müssen, gibt es ein Fehlerbehandlungskonzept, dass es
        dem Programmierer ermöglicht Ausnahmen, die sogenannten Exception, im
        Programmfluss zu “behandeln”. Er kann hierfür einen try-Block um kritische Funktionsaufrufe schreiben. Danach folgt ein catch-Block.
        Tritt während der Ausführung des Programms eine Exception auf, so
        stoppt die Ausführung an genau dieser Stelle wie bei einem
        Programmabsturz. Sie wird aber anschließend im catch-Block fortgesetzt.
    Im
        folgenden Beispiel verzichten wir auf die Prüfung, ob die Datei aus der
        wir lesen wollen existiert. Wir lesen einfach. Wenn die Datei dann
        nicht existiert, bricht das Programm ab und schreibt eine Fehlermeldung
        und läuft danach normal weiter.
File datei = new File("existiert-nicht.txt"); try { Scanner scnr = new Scanner(datei); while (scnr.hasNextLine()) { System.out.println(scnr.nextLine()); } scnr.close(); } catch (Exception e) { System.out.println("Datei konnte nicht gelesen werden: " + datei.getAbsolutePath()); }
Wie das Beispiel zeigt, wird beim Catch-Block, wie bei einer
        Methodendeklaration auch, ein Parameter definiert. In die dabei
        definierte Variable e wird ein Objekt übergeben, welches Informationen
        über die Fehlerursache liefert. Eine Beschreibung des Fehlers kann mit
        der getMessage() Methode abgefragt werden.
    Als weitere wichtige Komponente enthält das Exception-Objekt den sogenannten Stacktrace.
        Hierbei handelt es sich um eine Auflistung der Methoden-Aufrufe, die zu
        dem Fehler geführt haben. Das Konzept des Stacks funktioniert so, dass
        jedes Mal wenn die Ausführung einer Methode begonnen wird, ein Eintrag
        hinzugefügt wird. Sobald dann das Ende dieser Methode erreicht wird,
        wird der Eintrag wieder vom Stack entfernt. Man kann sich dies wie einen
        Stapel vorstellen, auf den immer oben ein Element drauf gelegt wird und
        immer nur das oberste Element wieder entfernt werden kann. Wenn ein
        Fehler auftritt, ist das oberste Element im Stack also der Ort an dem
        der Fehler aufgetreten ist. Das erste beziehungsweise unterste Element
        auf dem Stack ist immer die Main-Methode.
    Es ist immer
        sinnvoll den Stacktrace auszugeben, da er sehr wichtig für die
        Fehleranalyse und Programmverbesserung ist. Dies geschieht am
        einfachsten mit der Methode printStacktrace() die zum Beispie die
        folgende Ausgabe erzeugen könnte:
java.io.FileNotFoundException: existiert-nicht.txt (No such file or directory) at java.base/java.io.FileInputStream.open0(Native Method) at java.base/java.io.FileInputStream.open(FileInputStream.java:219) at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157) at java.base/java.util.Scanner.<init>(Scanner.java:639) at test.Main.leseDatei(Main.java:21) at test.Main.main(Main.java:11)
Die Ausgabe des Stacktrace beginnt mit einer Zeile in der der
        Fehler benannt wird. Sie beginnt mit dem Namen der Exceptionklasse, der
        einen ersten Hinweis auf den Ursprung des Problems gibt. Danach folgt
        eine Nachricht, die für diese spezielle Fehlerursache hinterlegt wurde.
        In den nachfolgenden Zeilen wird dann jeweils ein Funktionsaufruf
        dargestellt. Meistens genügt es, sich auf Zeilen zu konzentrieren, die
        mit dem eigenen Code zu tun haben. Um diese zu finden sucht man einfach
        nach Zeilen mit dem eigenen Package. In diesem Beispiel das Package
        “test”. Von diesen Zeilen ist die Oberste meist die entscheidende. In
        dieser Zeile meint “test”, das Package. Mit “Main” wird die Klasse
        benannt, in der der Fehler aufgetreten ist und “leseDatei” ist die
        Methode. In Klammern folgt noch einmal der Klassenname und die Zeile des
        Aufrufs.
    Wenn man diese Information richtig
        verstanden hat, kann man schnell die Ursache eines Fehlers finden und
        beheben. Die optimale Korrektur für unseren Fehler wäre, vor dem Zugriff
        auf die Datei zu prüfen, ob sie existiert und lesbar ist.
    Eine
        allgemeine Antwort, wie Fehler korrekt behandelt werden gibt es leider
        nicht. Als Richtwert kann man sagen, dass es besser ist Fehler zu
        vermeiden als sie nachträglich zu behandeln. Bei allen anderen Fehlern
        muss man überlegen, ob man den Zustand des Programms wieder so
        korrigieren kann, dass es ein korrektes Ergebnis liefert und diesen
        wieder herstellen. Wenn dies nicht möglich ist, sollte man nur eine
        aussagekräftige, verständliche Mitteilung für den Nutzer ausgeben und
        dann das Programm beenden.
    In einigen Situationen ist es nicht möglich eine Exception sinnvoll zu behandeln. Für diesen Fall sieht Java die throws-Erweiterung
        bei der Methodendeklaration vor. Wir hatten sie in der Vergangenheit
        schon öfter benutzt, damit der Compiler unsere Methoden akzeptiert.
    Manchmal
        ist es sinnvoll, dass spezieller Code in jedem Fall ausgeführt wird,
        unabhängig davon, ob nun ein Fehler aufgetreten ist oder nicht. Hierfür
        kann nach oder statt dem catch-Block ein finally-Block geschrieben werden. Was darin steht, wird in jedem Fall am Ende der Fehlerbehandlung ausgeführt.
try { leseDatei(new File("datei.txt")); } catch(Exception e) { // Fehler wird behandelt } finally { aufraeumen(); }
Hierarchie der Exceptions
Die Exceptions in Java sind durch verschiedene
        Klassen dargestellt. Diese Klassen wiederum sind in einer hierarchischen
        Struktur angeordnet. Es folgt eine Liste der für uns wichtigsten
        Exceptions.
    - Exception
Die Klasse Exception ist die Basisklasse. Immer wenn man sagen möchte, dass man irgendeine beliebige Exception abfangen möchte so gibt man diesen Typ an.- RuntimeException
Die Ausprägungen dieser Klasse können während der Ausführung eines Programms theoretisch überall auftreten. Aus diesem Grund erzwingt der Compiler auch keine Behandlung dieser Exceptions. Man nennt dies “unchecked” Exceptions. Alle anderen Exceptions sind “checked” Exceptions, für die der Compiler eine Behandlung erzwingt.- ArithmeticException
Sie tritt auf, wenn das Programm eine Berechnung durchführen soll, die mathematisch nicht möglich ist. Zum Beispiel dividieren durch 0. - NullPointerException
Eine NullPointerException tritt auf, wenn auf eine Eigenschaft oder eine Methode eines Objektes zugegriffen wird, das den Wert null hat. - IndexOutOfBoundsException
Bei der Verwendung von Arrays kann es passieren, dass man auf einen Index zugreifen möchte, der zu groß oder kleiner als 0 ist. In diesen Fällen tritt eine IndexOutOfBoundsException auf. 
 - ArithmeticException
 - IOException
Alle IOExceptions signalisieren, dass bei der Eingabe oder der Ausgabe von Daten ein Fehler aufgetreten ist.- FileNotFoundException
Sie signalisiert, dass eine Datei nicht existiert. - EOFException
Wird das Ende einer Eingabedatei unerwartet früh erreicht, so tritt eine EOFException auf. 
 - FileNotFoundException
 - ParseException
Bei dem Versuch aus einem String zum Beispiel eine Zahl der ein Datum zu extrahieren kann es zu Fehlern kommen, wenn der String nicht korrekt formatiert ist. 
 - RuntimeException
 
Auch wenn wir in diesem Kurs
        damit auskommen nur die Klasse Exception zu behandeln, wollen wir noch
        kurz auf die Möglichkeiten schauen, die uns diese Hierarche bietet.
    Bei
        der Deklaration von Methoden können nach dem throws Schlüsselwort
        mehrere Exception-Klassen durch Komma getrennt aufgeführt werden. So
        könnte man zum Beispiel angeben, dass ParseException und EOFException
        nicht behandeln wird. Der Compiler weiß dann aber, dass falls eine Datei
        geöffnet werden soll, er die Behandlung einer FileNotFoundException
        erzwingen soll.
public static int readNumberFromFile(File file) throws ParseException, EOFException { try { // lese Dateiinhalt und wandle ihn in eine Zahl um } catch (FileNotFoundException e) { // verwende einen Standardwert } }
Das Beispiel zeigt, wie man dem Compiler mitteilt, wie man mit unterschiedlichen Exceptionklassen umgehen möchte.
    Eine
        andere Möglichkeit ist, dass man in einem Try-Catch mehrere
        unterschiedliche Exceptionklassen behandeln kann. Dafür kann man mehrere
        catch-Blöcke aneinander reihen.
try { leseDatei(datei); } catch(NullpointerException e) { // könnte auftreten, wenn datei null ist } catch(FileNotFound e) { // könnte auftreten wenn die Datei nicht existiert } catch(IOException e) { // könnte auftreten wenn andere Leseprobleme auftreten } catch(Exception e) { // fängt alle übrigen Fehler auf }
Im Beispiel ist zu sehen, wie mehrere unterschiedliche Exceptionklassen
    abgefangen werden können. Es ist dabei wichtig, dass zuerst die
    speziellen Fälle benannt werden und erst später die allgemeinen Fälle.
Exceptions erstellen
Exceptions werden nicht nur von Java selbst
        erstellt, wir können auch eigene Exceptions erstellen. Hierfür erstellt
        man eine Instanz der gewünschten Exceptionklasse und “wirft” diese mit
        Hilfe des throw Schlüsselwortes. Bei der Instanziierung kann man einen String als Nachricht mit übergeben.
if(parameter < 0) { throw new RuntimeException("Der parameter darf nicht negativ sein."); }
Die Behandlung dieses Fehlers funktioniert dann wieder wie gehabt.