Jetzt anmelden...

Login
Passwort
Registrieren
13.12.20 619 Views Kommentare [0] 1 0

credit: ©Ivan Kurmyshov

Java Tutorial #39

Exceptions und Polymorphie

Die polymorphen Eigenschaften von Java-Variablen machen ein flexibles Exception Handling möglich. Von einem catch-Block, der alles abfängt, bis zur differenzierten Ausnahmebehandlung mit mehreren catch-Blöcken ist alles möglich.

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

  • Eine Referenzvariable vom Typ Exception kann alle Exception-Typen "auffangen" (=Polymorphie).
  • Für eine differenziertes Exception-Handling sollten mehrere catch-Blöcke eingesetzt werden.

    Inhaltsverzeichnis

  1. Polymorphie bei Exceptions
  2. Mehrere catch-Blöcke
  3. Übungen

Polymorphie bei Exceptions

Wir haben dem Thema der Polymorphie in Java bereits ein gesondertes Kapitel gewidmet. Die Quintessenz des Prinzips der Polymorphie lautet demnach:

  • Polymorphie bedeutet, dass eine Variable auf ein Objekt verweisen kann, dessen Typ entweder gleich dem deklarierten Typ der Referenzvariablen oder ein beliebiger Subtyp davon ist.

Auch im Bereich des Exception Handlings haben wir uns die polymorphen Eigenschaften von Variablen schon zu Nutze gemacht. Sehen wir uns doch einmal dieses Beispiel an:

try {
    double d = 2 / 0; // wirft ArithmeticException
} catch (Exception e) { // deklariert Superklasse
    e.printStackTrace();
}

Die Division durch 0 ist keine gültige arithmetische Operation, sodass zur Laufzeit eine ArithmeticException ausgelöst wird. Referenziert wird das geworfene ArithmeticException-Objekt aber durch eine Variable vom Typ Exception.

So funktioniert Polymorphie: Objekte vom Typ ArithmeticException sind kompatibel zum Referenztyp Exception, weil ArithmeticException - wie alle Exceptions - die Klasse Exception erweitert. Vereinfacht ausgedrückt: Eine ArithmeticException kann mit einer Variablen vom Typ Exception referenziert werden, da sie eine abgeleitete Klasse davon ist:

Java Exception checked unchecked

Mehrere catch-Blöcke

Ein catch für alle Fälle

Wenn wir im catch-Block die Superklasse Exception deklarieren, können wir Exceptions jedes beliebigen Typs auffangen.

Selbst, wenn der Code eines try-Blocks mehrere unterschiedliche Exception-Typen auslösen könnte, genügt so ein einziger catch-Block, um jede dieser Exceptions aufzufangen. Hier ein Beispiel:

public static void main(String[] args){

Scanner scanner = new Scanner(System.in);
System.out.println("Wollen sie die Datei lesen? 1 für ja");

    try {
        if(scanner.nextInt() == 1) { // riskant: InputMismatchException
            BufferedReader reader = new BufferedReader(new FileReader("info.txt")); // riskant: FileNotFoundException
            String s = reader.readLine(); // riskant: IOException
            System.out.println(s);
            reader.close();
        }
    }

    catch(Exception e) { // fängt alles!
        e.printStackTrace();
    }
}

Wie wir im Code sehen, könnten Exceptions von drei unterschiedlichen Typen ausgelöst werden. Dank Polymorphie genügt aber ein einziger catch-Block mit der Deklaration der Superklasse Exception. Diese fängt dann einfach alles ab!

So vorzugehen ist super praktisch und insbesondere zu Testzwecken oder "Quick-and-dirty"-Programmierung ein Vorteil.

Jedem das Seine

Allerdings ist das nicht der Königsweg. Denn oftmals benötigen unterschiedliche Exceptions auch eine unterschiedliche Behandlung. Vielleicht soll das Programm ja bei einer InputMismatchException anders reagieren als bei einer FileNotFoundException oder IOException. Mit einem allgemeinen catch-Block ist das nicht möglich, da alle Exceptions gleich behandelt werden.

Was wir brauchen sind mehrere catch-Blöcke mit einemn jeweils spezifischen Exception-Handling:

public static void main(String[] args){

Scanner scanner = new Scanner(System.in);
System.out.println("Wollen sie die Datei lesen? 1 für ja");

    try {
        if(scanner.nextInt() == 1) { // riskant: InputMismatchException
            BufferedReader reader = new BufferedReader(new FileReader("info.txt")); // riskant: FileNotFoundException
            String s = reader.readLine(); // riskant: IOException
            System.out.println(s);
            reader.close();
        }
    }

    catch(InputMismatchException e) {
        System.out.println("Fehlerhafte Eingabe! " + e);
    }
    catch(FileNotFoundException e) {
        System.out.println("Datei kann nicht gefunden werden. "  + e);
    }
    catch(IOException e) {
        System.out.println("Fehler beim Lesen der Datei."  + e);
    }
}

Wie wir sehen, gibt es nun drei unterschiedliche catch-Blöcke, die jede der drei unterschiedlichen Exception-Typen aus dem try-Block spezifisch auffangen können.

Natürlich nimmt unser Code allein durch die Behandlung von möglichen(!) Ausnahmesituation deutlich an Umfang zu. Aber ein sinnvolles Exception-Handling gehört zu einer guten Programmierung einfach dazu und ist eine Investition in die Stabilität des Programms und das Vertrauen des Endnutzers in das Softwareprodukt.

Exception has already been caught

Sehen wir uns doch einmal folgenden Code an. Was glauben Sie: Warum lässt er sich nicht compilieren?

try {
    int x = 5 / 0; // ArithmeticException
}
catch(Exception e){
    //
}
catch (ArithmeticException e){
    //
}

Java geht die einzelnen catch-Blöcke von oben nach unten durch und wählt denjenigen aus, in den die geworfene Exception zuerst "reinpasst".

In unserem Code gibt es zwei catch-Blöcke, die jeweils unterschiedliche Exception-Typen fangen. Allerdings steht die allgemeine Klasse Exception, wie sie im ersten catch-Block deklariert wird, in der Vererbungshierarchie über der Klasse ArithmeticException aus dem zweiten catch-Block. Das heißt: Das ArithmeticException-Objekt wird schon vom ersten catch-Block gefangen. Der Compiler weiß, dass eine Fortsetzung des Exception Handlings sinnlos ist. Der zweite catch-Block ist also nutzlos. Solch einen Code lässt der Compiler nicht durchgehen und wir erhalten die Compiler-Meldung "Exception has already been caught".

Übungen

einfach

Wozu sind mehrere catch-Blöcke beim Exception Handling sinnvoll?

Lösung ein-/ausblenden

mittel

Warum compiliert dieser Code nicht?

public static void main(String[] args) {

    String name = "Red Reddington";

    try {
        int x = Integer.parseInt(name);
    }
    catch(Exception e){
        System.out.println("A");
    }
    catch (NumberFormatException e){
        System.out.println("B");
    }
}
Lösung ein-/ausblenden

schwer

Was wird durch folgenden Code auf der Konsole angezeigt?

 public static void main(String[] args){

    String[] a = {"PS5", "XBX"};

    try{
        System.out.print("A");
        a[2] = "Switch";
        System.out.print("B");
    }
    catch(NullPointerException ex){
        System.out.print("C");
    }
    System.out.print("D");
    }

}

A. A
B. A und Exception-Meldung
C. AB
D. AB und Exception-Meldung
E. ABC
F. ABC und Exception-Meldung
G. ABCD
H. ABCD und Exception-Meldung

Lösung ein-/ausblenden

Falconbyte unterstützen

Kommentar schreiben

Alle Kommentare

Es gibt bislang noch keine Kommentare zu diesem Thema.

Scope von Variablen

Wie lange lebt eine Variable eigentlich?

Objektsammlungen

Lernen Sie in diesem Kapitel, wie Sie eine Sammlung von Objekten erstellen können.

Operatoren einsetzen

Es gibt eine Vielzahl an Operatoren. Wir haben eine Übersicht erstellt

FALCONBYTE.NET

Handmade with 🖤️

© 2018-2021 Stefan E. Heller

Impressum | Datenschutz

Falconbyte GitHub facebook programmieren lernen twitter programmieren lernen discord programmieren lernen