12.12.19 6795 Views Kommentare [] 8 0

credit: ©teerapon

Java Tutorial #38

Unterschied: unchecked und checked Exceptions

Man unterscheided zwei Gruppen vom Exceptions: Unchecked und checked Exceptions. Für die Behandlung von Ausnahmezuständen ist das Wissen über den unterschiedlichen Umgang beider Exception-Arten essentiell.

Falconbyte unterstüzen

Betrieb und Pflege von Falconbyte brauchen viel Zeit und Geld. Um dir auch weiterhin hochwertigen Content anbieten zu können, kannst du uns sehr gerne mit einem kleinen "Trinkgeld" unterstützen.

Thema in Kurzform

  • Unchecked Exceptions (dt. ungeprüfte Ausnahmesituationen) sind alle RuntimeExceptions. Diese werden zur Compilierzeit nicht geprüft. Das Programm ist auch ohne "Behandlung" lauffähig.
  • Checked Exceptions (dt. geprüfte Ausnahmesituationen) werden vom Compiler geprüft. Sie müssen vom Programmierer behandelt werden. Das Programm ist ansonsten nicht lauffähig.

    Inhaltsverzeichnis

  1. Exception Klassenhierarchie
  2. Unchecked Exceptions
  3. checked Exceptions
  4. Ein Beispiel für checked Exceptions
  5. Das Schlüsselwort throws
  6. Übungen

Exception Klassenhierarchie

Wie wir im letzten Kapitel gesehen haben, sind Exceptions "Ausnahmezustände", die den Programmfluss stören und das Programm zum Absturz bringen können. Alle Exceptions sind Instanzen der Klasse Exception und erweitern die Superklasse Throwable.

Von Exception werden wiederum zahlreiche Subklassen abgeleitet. Sehen wir uns einen Ausschnitt der Exception-Klassenhierarchie im Gesamtkontext einmal an:

Java Exception checked unchecked

In Java werden Exceptions in zwei Gruppen unterteilt: Unchecked Exceptions und checked Exceptions. Gehen wir zuerst der Frage nach, was unchecked Exceptions sind.

Unchecked Exceptions

Alle Klassen, die RuntimeException erweitern, sind unchecked Exceptions. Diese Exceptions treten zur Laufzeit (runtime) des Programms auf. Sie werden "unchecked" (dt. ungeprüft) genannt, weil der Compiler für diese zur Übersetzungszeit keine Überprüfung vornimmt.

Die meisten Runtime Exceptions resultieren aus Situationen, die vermeidbar gewesen wären, d.h. sie gehen zumeist auf logische Programmierfehler zurück. Es ist nicht die Aufgabe des Compilers, Sie hierbei zu unterstützen. Deshalb werden sie in Ihrer Programmierumgebung nicht als Fehler angezeigt und der Compiler lässt sie ohne Prüfung "unter dem Radar" durchgehen. Ein compilierbares Programm ist also nicht gleich auch fehlerfrei.

Sehen wir uns nur einige unchecked Exceptions bzw. RuntimeExceptions exemplarisch etwas genauer an.

ArithmeticException

Diese Exception wird "geworfen", wenn ein Integer-Wert durch 0 geteilt wird (Division durch 0).

int part = 4 / 0; // ArithmeticException

ArrayIndexOutOfBoundsException

Wer kennt sie nicht, die berühmte ArrayIndexOutOfBoundsException? Falls wir einen Index-Wert ansprechen, der außerhalb der Array-Grenzen liegt, wird diese Exception geworden:

String[] names = new String[4];
String name = names[4]; // ArrayIndexOutOfBoundsException

Es gibt in diesem Array mit der Länge 4 nur die Indices 0, 1, 2, 3. Alles andere ist außerhalb der gültigen Array-Grenze.

ClassCastException

Wenn wir versuchen, eine ungültige Typumwandlung (cast) durchzuführen, erhalten wir eine ClassCastException:

String s = "ABC";
Object o = s;
Integer i = (Integer) o; // ClassCastException

Der Compiler lässt diesen Code zur Übersetzungzeit durchgehen, da er hinsichtlich der statischen Situation in der dritten Zeile eine Typumwandlung von Object nach Integer sieht, was prinzipiell auch in Ordnung geht. Der Compiler kann aber nich wissen, dass Object o auf ein String-Objekt ("ABC") zeigt. Zur Laufzeit (dynamisch) wird der Code deshalb crashen, weil eine Umwandlung von String nach Integer schlicht ungültig ist (mehr zum Thema Typumwandlung).

NullPointerException

Methoden können nicht auf einer Null-Referenz aufgerufen werden. Null-Referenzen sind Referenzvariablen, die auf kein Objekt zeigen, also den Wert null haben:

public class Falcon {

    public String f;

    public void p(){
        System.out.println(f.length());
    }

    public static void main(String[] args){

        new Falcon().p(); // NullPointerException

    }

}

Der Methodenaufruf new Falcon().p(); wird eine NullPointerException auslösen, da die String-Referenz f auf kein String-Objekt zeigt (Wert = null)

NumberFormatException

Java stellt mit den Wrapper-Klassen Methoden bereit, die Strings in primitive Werte umwandeln können. Wenn dies mit ungültigen Werten versucht wird, wird eine NumberFormatException geworfen:

int x = Integer.parseInt("abc"); // NumberFormatException

mehr davon!

Im letzten Kapitel haben wir noch die InputMismatchException kennengelernt, die bei einer ungültigen Konsolen-Eingabe geworfen wird. Es gibt aber noch weitaus mehr Unterklassen von RuntimeException, die alle ihren speziellen Zweck haben. Wir konnten aus Platzgründen hier nicht alle im Detail besprechen. Sicherlich werden Sie im Laufe Ihrer Programmierkarriere aber noch die ein oder andere RuntimeException kennenlernen 😀

checked Exceptions

Alle Exceptions, die nicht Unterklassen von RuntimeException sind, gehören zur Gruppe der checked Exceptions. Was bedeutet das, wenn eine Exception als "checked" definiert ist?

Checked Exceptions sind "geprüfte" Exceptions, das heißt, dass der Compiler zur Übersetzungszeit eine Prüfung in Form eines try-catch-Mechanismus vom Programmierer einfordert. Fehlt das entsprechende Exception-Handling, ist der Code nicht compilierbar. Wir müssen also bestätigen, dass wir uns des Risikos eines bestimmten Methodenaufrufs bewusst sind. Eben das ist der Unterschied zu den RuntimeExceptions: Diese werden vom Compiler ignoriert und auch ohne Exception-Handling "ungeprüft" (unchecked) durchgelassen.

Checked Exceptions sind für Fälle gedacht, in denen wir damit rechnen sollten, dass etwas schieflaufen könnte. Wenn wir zum Beispiel eine Datei schreiben wollen, können wir nicht garantieren, dass die Datei z.B. nicht schreibgeschützt ist. Das liegt außerhalb der "Macht" unseres Programms. In solchen Fällen werden wir als Programmierer vom Compiler "gezwungen", den Erfolg des riskanten Vorhabens zu überprüfen.

Zu den checked Exception gehören unter anderen:

  • FileNotFoundException: Wird geworfen, wenn das Programm versucht, eine nicht vorhandene Datei zu öffnen.
  • IOException: Auslöser sind Probleme beim Lesen oder Schreiben einer Datei.
  • SQLException: Wird bei Problemen bei der Arbeit mit Datenbanken geworfen.

Ein Beispiel für checked Exceptions

Sehen wir uns ein Beispiel an, in dem wir es mit den beiden checked Exception-Typen FileNotFoundException und IOException zu tun bekommen könnten:

Mit dem folgenden Programm soll die erste Zeile einer Datei namens info.txt ausgelesen und auf der Konsole dargestellt werden. Wir greifen hier thematisch vor, da wir uns mit der Ein- und Ausgabe von Dateien erst im nächsten Kapitel beschäftigen werden. Wir halten es daher hier so knapp wie möglich:

try {
    BufferedReader reader = new BufferedReader(new FileReader("info.txt")); // Datei öffnen
    String s = reader.readLine(); // 1. Zeile auslesen
    System.out.println(s);
    reader.close();

} catch (Exception e) {
    e.printStackTrace();
}

Erklärung

Für das zeilenweise Auslesen der Datei verwenden wir einen BufferedReader. Da wir damit jedoch keine Dateien öffnen können, wird dem BufferedReader gleich bei der Instanziierung ein FileReader-Objekt als Argument mitgegeben. Dieses wird automatisch mit dem BufferedReader-Objekt gekapselt und übernimmt die Aufgabe, die Datei zu öffnen.

Mit der BufferedReader-Methode readline() wird dann eine Zeile aus der Datei gelesen. Wir speichern die erste Zeile der Datei in einer String-Variable und geben sie anschließend auf der Konsole aus. Am Ende schließen wir mit reader.close() die Datei wieder.

Für gewöhnlich wird beim Auslesen einer Datei nicht nur die erste Zeile benötigt, sondern der gesamte Inhalt. Um den Code aber einfach zu halten, verzichten wir hier darauf.

Exception Handling

In diesem Programm kann an mehreren Stellen etwas schiefgehen, das nicht in unserer Hand liegt:

Ausnahmesituation Auslöser-Methode Exception-Typ
Datei kann nicht geöffnet werden FileReader(String fileName) FileNotFoundException
Fehler beim Lesen der Datei readLine() IOException
Datei kann nicht geschlossen werden close() IOException

Wir rufen also drei riskante Methoden auf, die in einer Ausnahmesituation (d.h. bei Fehlschlag) jeweils eine checked Exception werfen. Deshalb muss jede von ihnen mit einem try-catch-Block "behandelt" werden. Der Compiler fordert das Exception Handling beim Einsatz dieser Methoden strikt ein; fehlt es, ist der Code nicht compilierbar.

Das Schlüsselwort throws

Anhand der Methodendeklaration der Java-API können wir ablesen, ob eine Methode eine checked Exception werfen kann - und falls ja: um welchen Exception-Typ es sich handelt. Am Beispiel der Methode readLine() sieht das so aus:

Java Exception throws

Neu ist das Schlüsselwort throws ("wirft"), das anzeigt, dass die Methode eine checked Exception werfen kann. Dahinter steht die Klasse des Exception-Typs.

Halten wir also fest:

  • Methoden, die mit dem Schlüsselwort throws deklariert sind, sind immer riskant.
  • Sie können checked Exceptions werfen.
  • Exceptions, die durch diese Methoden ausgelöst werden können, müssen zur Compilierzeit in jedem Fall behandelt werden.

Exception weiterwerfen

Bisher haben wir riskante Methoden immer direkt mit einem try-catch-Block umgeben. Exceptions müssen aber nicht "an Ort und Stelle" behandelt werden. Alternativ können wir die Exception auch "weiterwerfen".

public class Falcon {

public void dateiLesen() throws Exception{
        BufferedReader reader = new BufferedReader(new FileReader("info.txt")); // riskant!
        String s = reader.readLine(); // riskant!
        System.out.println(s);
        reader.close(); // riskant!
    }

    public static void main(String[] args){
        // Exception Handling:
        try {
            new Falcon().dateiLesen();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Wie wir sehen, behandelt die Methode dateiLesen() den riskanten Code nicht (es gibt kein try-catch-Statement). Was aber auffällt, ist, dass die Methodendeklaration von dateiLesen() um throws Exception erweitert wurde. Das bedeutet, dass die Exception an diejenige Methode weitergeworfen wird, die dateiLesen() aufruft. In unserem Beispiel ist das die Main-Methode - und genau hier findet das Exception Handling dann auch statt.

Wir können die Behandlung von checked Exceptions auf andere Methoden auf dem Stack auslagern, aber auslassen können wir sie nicht. Der Compiler fordert für den riskanten Code unbedingt ein Exception-Handling. Entweder direkt oder über das Weiterwerfen via throws-Deklaration.

Übungen

einfach

Was ist der Unterschied zwischen ungeprüften und geprüften Exceptions?

Lösung ein-/ausblenden

mittel

Welche Art von Exception wird hier geworfen?

try {
    Object obj = new Integer(1);
    String str = (String)obj;
} catch (Exception e) {
    //
}

A. ClassCastException
B. ArrayIndexOutOfBoundsException
C. InputMismatchException
D. IllegalArgumentException
E. keine!

Lösung ein-/ausblenden

schwer

Welche der folgenden Aussagen sind korrekt?

A. Checked und unchecked Exceptions erweitern diesselbe Klasse.
B. RuntimeExceptions müssen in das ExceptionHandling einbezogen werden.
C. Um das Exception Handling einer checked Exception auszusetzen, kann in der Methodendeklaration das Schlüsselwort throws eingebaut werden.
D. Sowohl geprüfte als auch ungeprüfte Exceptions müssen behandelt werden.
E. Alle Exceptions gehen auf Programmierfehler zurück.
F. Exceptions gehen nie auf Programmierfehler zurück.

Lösung ein-/ausblenden

Einstieg Objektorientierung

Lernen Sie die Grundlagen der Objektorientierung

Methoden in Java

Wie erstelle ich Methoden?

switch/ case Anweisung

Benötigen wir eine Unterscheidung nach vielen Fällen empfehlen sich switch-case-Statements.

FALCONBYTE.NET

Handmade with 🖤️

© 2018-2022 Stefan E. Heller

Impressum | Datenschutz | Changelog

Falconbyte GitHub facebook programmieren lernen twitter programmieren lernen discord programmieren lernen