EMZETT.
Login

Kurz: Wenn etwas schiefgeht (Datei fehlt, Division durch 0, ungültige Eingabe), löst Java eine Ausnahme (Exception) aus. Sie bricht den normalen Ablauf ab und springt zum nächsten passenden catch.

Teil des Kurses Java

Kapitel 14 von 22 im Kurs Java. Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

Wenn etwas schiefgeht (Datei fehlt, Division durch 0, ungültige Eingabe), löst Java eine Ausnahme (Exception) aus. Sie bricht den normalen Ablauf ab und springt zum nächsten passenden catch.

public class Fangen {
    public static void main(String[] args) {
        try {
            System.out.println("vorher");
            int x = Integer.parseInt("abc");
            System.out.println("wird nie erreicht " + x);
        } catch (NumberFormatException e) {
            System.out.println("Ungültige Zahl: " + e.getMessage());
        } finally {
            System.out.println("läuft immer");
        }
        System.out.println("Programm läuft weiter");
    }
}

Ausgabe:

vorher
Ungültige Zahl: For input string: "abc"
läuft immer
Programm läuft weiter

Mehrere catch-Blöcke

public class Mehrere {
    static void test(int variante) {
        try {
            switch (variante) {
                case 0 -> System.out.println(10 / (variante));
                case 1 -> { String s = null; s.length(); }
                case 2 -> { int[] a = new int[2]; a[5] = 1; }
                case 3 -> Integer.parseInt("x");
                default -> throw new IllegalStateException("Zustand " + variante);
            }
        } catch (ArithmeticException | NullPointerException e) {          // mehrere Typen
            System.out.println("Rechen/Null: " + e.getClass().getSimpleName());
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("Index: " + e.getMessage());
        } catch (RuntimeException e) {                                      // allgemeiner zuletzt
            System.out.println("Sonstiges: " + e.getClass().getSimpleName() + " " + e.getMessage());
        }
    }
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) test(i);
    }
}

Ausgabe:

Rechen/Null: ArithmeticException
Rechen/Null: NullPointerException
Index: Index 5 out of bounds for length 2
Sonstiges: NumberFormatException For input string: "x"
Sonstiges: IllegalStateException Zustand 4

Reihenfolge: spezifisch vor allgemein. Ein allgemeiner Typ (Exception) vor einem speziellen würde diesen verdecken (Compilerfehler).

Die Hierarchie

Throwable
 ├─ Error                    schwere JVM-Fehler (OutOfMemoryError, StackOverflowError), nicht fangen
 └─ Exception
     ├─ RuntimeException     ungeprüft (unchecked): Programmierfehler
     │   ├─ NullPointerException, IllegalArgumentException, IllegalStateException
     │   ├─ IndexOutOfBoundsException, ArithmeticException, ClassCastException
     │   └─ NumberFormatException, UnsupportedOperationException
     └─ andere Exceptions    geprüft (checked): IOException, SQLException ...
ArtBeispielePflicht?
Checked (geprüft)IOException, SQLExceptionmusst du fangen oder mit throws weitergeben
Unchecked (RuntimeException)NullPointerException, IllegalArgumentExceptionkeine Pflicht

throws und throw

import java.io.*;
 
public class Werfen {
    static double teile(double a, double b) {
        if (b == 0) throw new ArithmeticException("Division durch 0");
        return a / b;
    }
    static String lese(String pfad) throws IOException {         // checked: Aufrufer muss reagieren
        try (BufferedReader r = new BufferedReader(new FileReader(pfad))) {
            return r.readLine();
        }
    }
    public static void main(String[] args) {
        try {
            System.out.println(teile(10, 4));
            System.out.println(teile(1, 0));
        } catch (ArithmeticException e) {
            System.out.println("Fehler: " + e.getMessage());
        }
        try {
            lese("gibt-es-nicht.txt");
        } catch (IOException e) {
            System.out.println("IO: " + e.getClass().getSimpleName());
        }
    }
}

Ausgabe:

2.5
Fehler: Division durch 0
IO: FileNotFoundException

try-with-resources

Dateien, Verbindungen und Streams müssen geschlossen werden. Mit try (...) passiert das automatisch, auch bei Fehlern. Das Objekt muss AutoCloseable sein:

public class Ressource {
    static class Verbindung implements AutoCloseable {
        final String name;
        Verbindung(String name) { this.name = name; System.out.println("öffne " + name); }
        void sende(String s) { System.out.println(name + " sendet " + s); if (s.isEmpty()) throw new IllegalArgumentException("leer"); }
        @Override public void close() { System.out.println("schließe " + name); }
    }
    public static void main(String[] args) {
        try (Verbindung a = new Verbindung("A"); Verbindung b = new Verbindung("B")) {
            a.sende("Hallo");
            b.sende("");
        } catch (IllegalArgumentException e) {
            System.out.println("Fehler: " + e.getMessage());
        }
    }
}

Ausgabe:

öffne A
öffne B
A sendet Hallo
B sendet
schließe B
schließe A
Fehler: leer

Geschlossen wird in umgekehrter Reihenfolge der Öffnung.

Eigene Ausnahmen

public class Eigene {
    static class GuthabenException extends Exception {
        private final double fehlbetrag;
        GuthabenException(String nachricht, double fehlbetrag) {
            super(nachricht);
            this.fehlbetrag = fehlbetrag;
        }
        double getFehlbetrag() { return fehlbetrag; }
    }
    static double abheben(double stand, double betrag) throws GuthabenException {
        if (betrag > stand) throw new GuthabenException("Zu wenig Guthaben", betrag - stand);
        return stand - betrag;
    }
    public static void main(String[] args) {
        try {
            System.out.println(abheben(100, 30));
            System.out.println(abheben(100, 130));
        } catch (GuthabenException e) {
            System.out.println(e.getMessage() + ", es fehlen " + e.getFehlbetrag());
        }
    }
}

Ausgabe:

70.0
Zu wenig Guthaben, es fehlen 30.0

Ursache mitgeben (Exception Chaining)

public class Verkettet {
    static void lade() {
        try {
            Integer.parseInt("kaputt");
        } catch (NumberFormatException e) {
            throw new IllegalStateException("Konfiguration ungültig", e);   // Ursache bleibt erhalten
        }
    }
    public static void main(String[] args) {
        try { lade(); }
        catch (IllegalStateException e) {
            System.out.println(e.getMessage() + " <- " + e.getCause().getClass().getSimpleName());
        }
    }
}

Ausgabe:

Konfiguration ungültig <- NumberFormatException

Der Stacktrace

Nicht gefangene Ausnahmen beenden das Programm und zeigen den Stacktrace: die Kette der Methodenaufrufe bis zur Fehlerstelle.

public class Absturz {
    static int teile(int a, int b) { return a / b; }
    public static void main(String[] args) {
        System.out.println(teile(1, 0));
    }
}

Fehlermeldung:

Exception in thread "main" java.lang.ArithmeticException: / by zero
	at Absturz.teile(Absturz.java:2)
	at Absturz.main(Absturz.java:4)

Lies ihn von oben: Die erste Zeile nennt den Fehler, die nächste die Stelle, wo er auftrat.

Gute Praxis

  • Nur fangen, was du behandeln kannst. Ein leeres catch ist fast immer ein Fehler.
  • Fange nicht Exception oder Throwable pauschal.
  • Prüfe Eingaben vorher (IllegalArgumentException), statt Fehler abzufangen.
  • Gib Ausnahmen aussagekräftige Meldungen und die Ursache mit.
  • Nutze try-with-resources für alles, was geschlossen werden muss.
  • Logge Fehler (mit Stacktrace), statt e.printStackTrace() in Produktionscode.

Merke

  • try { } catch (Typ e) { } finally { }; throw new ... löst aus, throws meldet an
  • Checked Exceptions musst du behandeln, RuntimeExceptions nicht
  • Spezifische catch-Blöcke vor allgemeinen
  • try-with-resources schließt Ressourcen automatisch
  • Eigene Ausnahmen erweitern Exception oder RuntimeException; Ursache mitgeben

Übungsaufgabe

Schreibe eine Methode leseZahl(String), die bei ungültiger Eingabe eine eigene Ausnahme wirft, und fange sie in einer Schleife über mehrere Eingaben ab.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Records, Enums und versiegelte Typen

Weiter: Generics

Alle Kapitel: Java im Überblick