CodeGym /Kurse /JAVA 25 SELF /Ausnahmen als Teil der API und try-with-resources

Ausnahmen als Teil der API und try-with-resources

JAVA 25 SELF
Level 24 , Lektion 4
Verfügbar

1. Ausnahmen als Teil der API

Warum sind Ausnahmen Teil des „Vertrags“ einer Methode?

Wenn Sie eine Methode schreiben, definieren Sie nicht nur Parameter und Rückgabewert, sondern auch welche Ausnahmen sie auslösen kann. Das ist Teil des „Vertrags“ zwischen Ihrer Methode und denjenigen, die sie verwenden. Wenn eine Methode eine Ausnahme auslösen kann, sollten das alle Aufrufer wissen – damit sie den Fehler korrekt behandeln (oder sich zumindest nicht wundern, wenn das Programm unerwartet mit einem Fehler beendet).

Beispiel:

public void readFile(String filename) throws IOException {
    // ... Datei lesen
}

Hier ist ausdrücklich angegeben, dass die Methode IOException auslösen kann. Das ist ein Signal an den Nutzer der Methode: „Sei darauf vorbereitet, den Datei-Lesefehler zu behandeln!“

Dokumentation von Ausnahmen: Annotation @throws in Javadoc

Damit andere Entwickler verstehen, welche Ausnahmen Ihre Methode auslösen kann, verwenden Sie die Annotation @throws (oder @exception) in Javadoc.

Beispiel:

/**
 * Liest den Inhalt einer Datei.
 *
 * @param filename Dateiname
 * @return den Dateiinhalt als String
 * @throws IOException wenn ein Lesefehler aufgetreten ist
 */
public String readFile(String filename) throws IOException {
    // ...
}

Wozu ist das nötig?

  • Hilft anderen Entwicklern zu verstehen, welche Fehler behandelt werden müssen.
  • IDEs und Dokumentationsgeneratoren (z. B. Javadoc) zeigen diese Ausnahmen direkt in Tooltips an.
  • Erhöht die Zuverlässigkeit und Vorhersehbarkeit des Codes.

Checked- und Unchecked-Ausnahmen in der API

Checked-Ausnahmen (Subtypen von Exception, aber nicht von RuntimeException) sind Teil des Methodenvertrags. Sie müssen entweder behandelt oder ausdrücklich weitergeworfen werden (throws).

Unchecked-Ausnahmen (RuntimeException und Subtypen) signalisieren üblicherweise Programmfehler (z. B. NullPointerException, IllegalArgumentException). Sie müssen nicht in der Signatur angegeben werden, aber wenn Ihre Methode eine solche Ausnahme auslösen kann (z. B. bei ungültigen Argumenten), sollten Sie das ebenfalls in Javadoc beschreiben.

Beispiel:

/**
 * Teilt a durch b.
 * @param a Dividend
 * @param b Divisor
 * @return Ergebnis der Division
 * @throws IllegalArgumentException wenn b == 0
 */
public int divide(int a, int b) {
    if (b == 0) throw new IllegalArgumentException("Der Divisor darf nicht 0 sein");
    return a / b;
}

Ausnahmen und API-Design

  • Denken Sie an den Nutzer Ihrer Methode: Welche Fehler kann und soll er behandeln? Welche sind Bugs, und welche sind „normale“ Situationen?
  • Übertreiben Sie es nicht mit Checked-Ausnahmen: Wenn der Fehler ein Bug ist (z. B. ein ungültiges Argument), werfen Sie besser eine Unchecked-Ausnahme.
  • Dokumentieren Sie alle Ausnahmen, die „nach oben“ weitergegeben werden können.

2. Konstruktion try-with-resources

Problem: Wie schließt man Ressourcen sicher?

In vielen Aufgaben arbeitet man mit Ressourcen, die nach der Verwendung unbedingt geschlossen werden müssen: Dateien, Netzwerkverbindungen, Datenbanken usw. Wenn man vergisst, eine Ressource zu schließen, kann das zu Speicherlecks, Dateisperren oder anderen Problemen führen.

Früher:

BufferedReader reader = null;
try {
    reader = new BufferedReader(new FileReader("data.txt"));
    String line = reader.readLine();
    // ...
} catch (IOException e) {
    // Fehlerbehandlung
} finally {
    if (reader != null) {
        try {
            reader.close();
        } catch (IOException e) {
            // Fehler beim Schließen behandeln
        }
    }
}

Viel Code, leicht fehleranfällig, man kann das Schließen der Ressource vergessen.

Lösung: try-with-resources

Seit Java 7 gibt es die Konstruktion try-with-resources, die alle Ressourcen automatisch schließt – selbst wenn innerhalb des Blocks eine Ausnahme auftritt.

Syntax:

try (ResourceType resource = new ResourceType(...)) {
    // Arbeit mit der Ressource
} catch (ExceptionType e) {
    // Fehlerbehandlung
}

Beispiel:

try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
    String line = reader.readLine();
    System.out.println(line);
} catch (IOException e) {
    System.out.println("Fehler beim Lesen der Datei: " + e.getMessage());
}
// reader.close() wird automatisch aufgerufen!

Wie funktioniert das?

  • In den Klammern nach try werden die Ressourcen deklariert, die geschlossen werden sollen.
  • Nach Verlassen des try-Blocks (selbst wenn eine Ausnahme auftritt!) wird für jede Ressource die Methode close() aufgerufen.
  • Das funktioniert nur für Ressourcen, die das Interface AutoCloseable (oder dessen Vorfahren Closeable) implementieren.

Interface AutoCloseable:

public interface AutoCloseable {
    void close() throws Exception;
}

Alle Standardressourcen von Java (Dateien, Streams, Datenbankverbindungen) implementieren dieses Interface.

Mehrere Ressourcen deklarieren

try (
    BufferedReader reader = new BufferedReader(new FileReader("input.txt"));
    BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))
) {
    String line;
    while ((line = reader.readLine()) != null) {
        writer.write(line);
        writer.newLine();
    }
}

Beide Ressourcen werden automatisch geschlossen, selbst wenn eine Ausnahme auftritt.

Vorteile von try-with-resources

  • Sicherheit: Ressourcen werden immer geschlossen, auch bei Fehlern.
  • Kürze: Weniger Code, geringere Fehlerwahrscheinlichkeit.
  • Lesbarkeit: Es ist sofort ersichtlich, welche Ressourcen verwendet werden und wann sie geschlossen werden.

3. Praxis: sicheren Code mit try-with-resources schreiben

Beispiel: Datei lesen

public static void printFirstLine(String filename) {
    try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
        String line = reader.readLine();
        System.out.println("Erste Zeile: " + line);
    } catch (IOException e) {
        System.out.println("Fehler: " + e.getMessage());
    }
}

Beispiel: in Datei schreiben

public static void writeToFile(String filename, String text) {
    try (BufferedWriter writer = new BufferedWriter(new FileWriter(filename))) {
        writer.write(text);
    } catch (IOException e) {
        System.out.println("Fehler beim Schreiben: " + e.getMessage());
    }
}

Beispiel: eigene Ressource

Wenn Sie eine eigene Klasse schreiben, die geschlossen werden muss, implementieren Sie einfach AutoCloseable:

public class MyResource implements AutoCloseable {
    @Override
    public void close() {
        System.out.println("Ressource geschlossen!");
    }
}

Jetzt kann er in try-with-resources verwendet werden:

try (MyResource res = new MyResource()) {
    // mit der Ressource arbeiten
} 

4. Typische Fehler und Best Practices

Fehler Nr. 1: Ressource nicht geschlossen (ohne try-with-resources).
Ohne try-with-resources vergisst man leicht, eine Datei oder einen Stream zu schließen – das führt zu Ressourcenlecks.

Fehler Nr. 2: Versuch, try-with-resources mit einem Objekt zu verwenden, das AutoCloseable nicht implementiert.
Wenn Ihre Klasse dieses Interface nicht implementiert, lässt der Compiler die Verwendung in try-with-resources nicht zu.

Fehler Nr. 3: Ausnahmen in der API nicht dokumentieren.
Wenn Ihre Methode eine Ausnahme auslösen kann – geben Sie das unbedingt in der Signatur (throws) und in Javadoc (@throws) an. Das hilft anderen, Ihren Code korrekt zu verwenden.

Fehler Nr. 4: Exception anstelle konkreter Fehler abfangen.
Fangen Sie besser nur die Ausnahmen, die Sie wirklich erwarten und behandeln können.

1
Umfrage/Quiz
Ausnahmehierarchie, Level 24, Lektion 4
Nicht verfügbar
Ausnahmehierarchie
Fortgeschrittene Arbeit mit Exceptions
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION