Ausnahmen und Dateien in Java

Lerne, wie Java Probleme während der Programmausführung meldet und wie du damit umgehst. Lies Stacktraces, unterscheide geprüfte von ungeprüften Ausnahmen und behandle Fehler mit try, catch und finally. Wirf eigene Ausnahmen und entwirf Ausnahmeklassen, die genau angeben, was schiefgegangen ist. Lies und schreibe anschließend Textdateien und behalte dabei Pfade, Zeichenkodierungen und Zeilenenden im Blick. Schließe jede Datei sicher mit try-with-resources und entwickle ein Programm, das mit fehlenden Dateien und fehlerhaften Daten umgehen kann.

AnfängerEinheit 9 von 11 in Kurs: Java-Programmierung – Klassen, Vererbung, Collections und Streams

Was du lernst

  1. Erkenne eine Ausnahme, wenn ein Programm fehlschlägt, lies den Stacktrace, um Ort und Ursache zu finden, und ordne jede Ausnahme in Javas Klassenhierarchie als geprüft oder ungeprüft ein.
    • Lies eine Ausnahme und ihren Stacktrace
      Erkläre, was geschieht, wenn eine Anweisung eine Ausnahme wirft, nenne häufige Laufzeitausnahmen und ihre Ursachen und finde das Problem mithilfe eines Stacktraces.
    • Ordne Ausnahmen als geprüft oder ungeprüft ein
      Ordne jede Ausnahme in die Hierarchie unterhalb von Throwable ein, entscheide, ob sie geprüft oder ungeprüft ist, und wende die Regel an, dass geprüfte Ausnahmen abgefangen oder deklariert werden müssen.
  2. Fange Ausnahmen ab, mit denen ein Programm rechnen kann, setze das Programm sinnvoll fort, behandle mehrere Arten in der richtigen Reihenfolge und führe unabhängig vom Ausgang Aufräumarbeiten aus.
    • Fange eine Ausnahme ab und setze das Programm sinnvoll fort
      Schreibe eine try-catch-Anweisung, verfolge ihren Kontrollfluss und gehe mit fehlerhaften Eingaben um, statt das Programm abstürzen zu lassen.
    • Behandle mehrere Ausnahmetypen in der richtigen Reihenfolge
      Verwende mehrere catch-Klauseln oder einen Multi-Catch, ordne sie vom spezifischen zum allgemeinen Typ und entscheide, wo ein try-Block beginnen und enden soll.
    • Führe Aufräumarbeiten mit finally aus
      Ergänze einen finally-Block, verfolge, wann er auf jedem Pfad durch eine try-Anweisung ausgeführt wird, und vermeide Fehler, durch die finally Probleme verbirgt.
  3. Signalisiere Probleme aus eigenen Methoden mit throw, deklariere geprüfte Ausnahmen mit throws und erstelle Ausnahmeklassen, die Aufrufern genau mitteilen, was schiefgegangen ist.
    • Wirf Ausnahmen und deklariere sie mit throws
      Prüfe Argumente und Objektzustände, indem du Standardausnahmen wirfst, deklariere geprüfte Ausnahmen in Methodenköpfen und entscheide, wo eine Ausnahme behandelt werden soll.
    • Erstelle eigene Ausnahmeklassen und erhalte die Ursache
      Schreibe eigene geprüfte oder ungeprüfte Ausnahmeklassen, wirf sie vor einer Änderung des Objektzustands und verpacke eine tieferliegende Ausnahme, ohne sie zu verlieren.
  4. Verweise in einem Programm auf die richtige Datei, verstehe, wie Text als Bytes und Zeilen gespeichert wird, und stelle sicher, dass jede geöffnete Datei mit try-with-resources geschlossen wird.
    • Arbeite mit Pfaden, Zeichenkodierungen und Zeilenenden
      Erstelle Pfade zu Dateien, sage voraus, wo Java bei einem relativen Pfad sucht, und erkläre, wie Zeichenkodierungen, Zeilenenden und Zahlenformate Textdateien beeinflussen.
    • Schließe Ressourcen automatisch mit try-with-resources
      Öffne und schließe Dateien und andere Ressourcen mit try-with-resources, sage die Reihenfolge beim Schließen voraus und erkläre, wie dabei verhindert wird, dass Ausnahmen verloren gehen.
  5. Lies Textdateien zeilenweise und wandle ihre Zeilen in Daten um, schreibe Ergebnisse in Dateien oder hänge sie an und verbinde alles zu einem Programm, das mit fehlenden Dateien und fehlerhaften Datensätzen umgehen kann.
    • Lies eine Textdatei ein und wandle ihre Zeilen in Daten um
      Lies eine Datei mit Scanner, Files.readAllLines oder BufferedReader, teile jede Zeile auf und wandle ihre Bestandteile um. Überspringe oder melde Zeilen, die nicht dem erwarteten Format entsprechen.
    • Schreibe Textdateien und hänge Text an bestehende Dateien an
      Schreibe Text in eine neue oder vorhandene Datei, hänge Text an, ohne den vorhandenen Inhalt zu löschen, und stelle sicher, dass alles Geschriebene tatsächlich die Datei erreicht.
    • Entwickle ein robustes Programm zur Dateiverarbeitung
      Verbinde Lesen, Parsen, Ausnahmen und Schreiben in einem Programm, das bei Dateifehlern geordnet stoppt, fehlerhafte Datensätze überspringt und meldet und unvollständige Ausgaben vermeidet.

Ausnahmen und Dateien in Java einfach erklärt

5 Kapitel

1

Verstehe, was Ausnahmen sind und wie Java sie einordnet

Ein Programm kann erfolgreich kompilieren und dennoch fehlschlagen, wenn es ein leeres Array, ungültigen Text oder eine fehlende Datei erhält. Das Ausnahmeobjekt und sein Stacktrace geben dir Hinweise auf diesen Fehler. Lies den Stacktrace durch die aktiven Methodenaufrufe zurück und entscheide dann anhand der Klassenhierarchie, ob Java beim Kompilieren eine Entscheidung über die Behandlung verlangt. So kannst du Programmfehler, die du beheben musst, von erwarteten Problemen unterscheiden, nach denen dein Programm sinnvoll fortfahren kann.

1.1 Finde die fehlschlagende Anweisung

Eine Ausnahme ist ein Objekt, das ein Problem beschreibt, das ein laufendes Programm unterbricht. Wird sie geworfen, stoppt die aktuelle Anweisung. Java überspringt die übrigen normalen Arbeitsschritte, bis es eine passende Ausnahmebehandlung findet; ohne eine solche wird die Ausnahme durch die aufrufenden Methoden zurückgegeben.

Erkenne die Art des Problems

Ein fehlendes Semikolon ist ein Kompilierfehler: Das Programm kann nicht kompiliert werden. Eine Ausnahme tritt auf, wenn kompilierter Code mit Daten ausgeführt wird, die ein Problem auslösen.

AusnahmeTypischer Auslöser
ArithmeticExceptionGanzzahldivision durch null
ArrayIndexOutOfBoundsExceptiona[3], wenn das Array die Länge 3 hat
IndexOutOfBoundsExceptionlist.get(list.size())
NullPointerExceptionAufruf einer Methode über null
NumberFormatExceptionInteger.parseInt("12a")
InputMismatchExceptionScanner.nextInt() trifft auf „ten“
ClassCastExceptionTypumwandlung eines Objekts in eine Klasse, zu der es nicht gehört

Array-Indizes beginnen bei 0, die Länge 3 bedeutet also die gültigen Indizes 0, 1 und 2. Für die Gleitkommadivision gelten andere Regeln: 10.0 / 0 ergibt Infinity; dabei wird nicht die Ausnahme der Ganzzahldivision geworfen.

Lies einen Stacktrace von oben

Hier ist eine vollständige Klasse ohne Leerzeilen vor der ersten Zeile:

public class Average {
    static int average(int[] values) {
        int sum = 0;
        for (int value : values) sum += value;
        return sum / values.length;
    }
    public static void main(String[] args) {
        int[] values = new int[0];
        System.out.println(average(values));
    }
}

Das Array ist leer, seine Länge ist also 0. Die Division in Zeile 5 schlägt fehl. Der Stacktrace lautet:

Exception in thread "main" java.lang.ArithmeticException: / by zero
    at Average.average(Average.java:5)
    at Average.main(Average.java:9)
  1. Lies in der ersten Zeile die Ausnahmeklasse und die Meldung.
  2. Lies im obersten at-Stapelrahmen die Stelle, an der das Problem erkannt wurde.
  3. Lies weiter nach unten zu den Aufrufern: main hat average in Zeile 9 aufgerufen.
  4. Gehören die obersten Stapelrahmen zur Java-Bibliothek, suche den ersten Stapelrahmen, der deine eigene Klasse nennt. Bei einem fehlgeschlagenen Integer.parseInt können Bibliotheksrahmen vor dem Aufruf in deinem Code stehen.

Der Stacktrace folgt dem Aufrufstapel, der Kette aktiver Methodenaufrufe. Er zeigt dir, wo du suchen solltest, aber der fehlerhafte Wert kann früher entstanden sein. Hier erkennt die Division das Problem; der Aufrufer hat das leere Array übergeben. Entscheide, ob eine leere Eingabe vor der Berechnung eines Mittelwerts abgelehnt oder behandelt werden soll.

Untersuche das Ausnahmeobjekt

e.getMessage() gibt die Meldung zurück und kann null zurückgeben. e.toString() enthält den Klassennamen und, falls vorhanden, die Meldung. e.printStackTrace() gibt den Stacktrace auf dem Standardfehlerstrom aus. Ein Abschnitt Caused by: zeigt eine frühere Ausnahme, die innerhalb einer Hülle erhalten blieb.

Von der JVM erzeugte NullPointerException-Meldungen benennen normalerweise den Ausdruck, der null ist. Eine ausdrücklich erzeugte Ausnahme kann stattdessen eine eigene Meldung oder keine Meldung haben. Verfolge die Referenz zurück bis zu der Stelle, an der sie ein Objekt hätte erhalten sollen.

Eine unbehandelte Ausnahme, die main verlässt, beendet den Hauptthread; bei einem Programm mit nur einem Thread endet damit das Programm. Behebe Programmfehler wie falsche Indizes im Code. Plane eine sinnvolle Fortsetzung für erwartete externe Probleme wie eine fehlende Datei oder eine ungültige Benutzereingabe.

Wichtige Erkenntnisse

  • Eine Ausnahme unterbricht die normale Ausführung; übersprungene Anweisungen werden nicht automatisch fortgesetzt.
  • Lies die Ausnahmeklasse und die Meldung und verfolge dann die Stapelrahmen vom erkannten Fehler bis zu seinen Aufrufern.
  • Stapelrahmen der Bibliothek können vor dem ersten Stapelrahmen deines eigenen Codes erscheinen.
  • Die fehlschlagende Anweisung kann einen fehlerhaften Wert aufdecken, der früher entstanden ist.
  • Behebe Programmfehler und behandle erwartete Eingabe- oder Dateifehler.

1.2 Geprüfte und ungeprüfte Ausnahmen

Für Ausnahmeklassen gelten die Vererbungsregeln, die du bereits kennst. Ein geworfenes Objekt einer Unterklasse gehört auch zu seiner Oberklasse. Das erklärt sowohl Javas Ausnahmekategorien als auch, wie eine Ausnahmebehandlung dazu passt.

Verfolge die Hierarchie

Nur Objekte von Throwable oder seinen Unterklassen können geworfen werden. Darunter liegen zwei Hauptzweige:

Throwable
  Error
    OutOfMemoryError
    StackOverflowError
  Exception
    RuntimeException
      IllegalArgumentException
        NumberFormatException
      NullPointerException
      IndexOutOfBoundsException
        ArrayIndexOutOfBoundsException
    IOException
      FileNotFoundException
      NoSuchFileException

Error beschreibt schwerwiegende Fehler der Laufzeitumgebung. Eine gewöhnliche Anwendung sollte nicht versuchen, nach einem ausgeschöpften Heap oder einem durch endlose Rekursion erschöpften Stapel fortzufahren. Exception beschreibt Probleme, die eine Anwendung behandeln kann, einschließlich des RuntimeException-Zweigs.

KategorieKlassenAnforderung des Compilers
UngeprüftRuntimeException, Error und ihre UnterklassenAbfangen oder Deklarieren ist optional
GeprüftAlle anderen Throwable-UnterklassenAbfangen oder deklarieren, wenn der Code sie werfen kann

Beide Arten werden zur Laufzeit geworfen. Das Wort „geprüft“ beschreibt die Prüfung deiner Behandlung durch den Compiler und keinen früheren Zeitpunkt, zu dem der Fehler auftritt. Ungeprüft bedeutet auch nicht harmlos: Eine unbehandelte Nullreferenz kann ein Programm stoppen.

Wende Abfangen oder Deklarieren an

Das Lesen einer Datei kann auch bei korrektem Code fehlschlagen. Eine Hilfsmethode kann die Entscheidung ihrem Aufrufer überlassen:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

class Names {
    static List<String> load(Path file) throws IOException {
        return Files.readAllLines(file);
    }
}

Files.readAllLines kann die geprüfte IOException werfen. Die Deklaration gibt diese Möglichkeit an den Aufrufer weiter, der sie ebenfalls abfangen oder deklarieren muss. Wird throws IOException entfernt, ohne eine Ausnahmebehandlung hinzuzufügen, lässt sich die Hilfsmethode nicht kompilieren. Für eine Methode, die Integer.parseInt verwendet, besteht keine vergleichbare Pflicht durch den Compiler, weil NumberFormatException ungeprüft ist.

Geprüfte Ausnahmen beschreiben oft Umstände, auf die sich ein Aufrufer einstellen kann, etwa das Verschwinden einer Datei. Laufzeitausnahmen weisen oft auf Programmfehler oder ungültige Argumente hin. Das sind hilfreiche Gründe für den Entwurf, aber die Oberklassenkette bestimmt die Kategorie. Eine eigene Klasse, die RuntimeException erweitert, bleibt ungeprüft, selbst wenn ein Aufrufer das Programm sinnvoll fortsetzen könnte.

Ordne Ausnahmebehandlungen anhand der Vererbung zu

catch (IOException e) kann eine FileNotFoundException oder NoSuchFileException behandeln. catch (Exception e) fängt auch Laufzeitausnahmen ab; es kann einen Programmfehler in eine irreführende Eingabemeldung verwandeln. catch (Throwable e) erreicht auch den Error-Zweig und ist für eine gewöhnliche Fortsetzung selten angebracht.

Um eine unbekannte Klasse einzuordnen, verfolge ihre Vererbung in der API-Dokumentation nach oben. InputMismatchException erweitert NoSuchElementException, das RuntimeException erweitert, und ist daher ungeprüft. InterruptedException erweitert Exception außerhalb des Laufzeitzweigs und ist daher geprüft. Einen ungeprüften Typ in einer throws-Klausel zu deklarieren, ist erlaubt, ändert aber seine Kategorie nicht.

Wichtige Erkenntnisse

  • RuntimeException, Error und alle ihre Unterklassen sind ungeprüft.
  • Andere Throwable-Unterklassen sind geprüft; Code, der sie werfen kann, muss sie abfangen oder deklarieren.
  • Sowohl geprüfte als auch ungeprüfte Ausnahmen treten zur Laufzeit auf.
  • Eine catch-Klausel für eine Oberklasse passt auch zu deren Unterklassen.
  • Eine allgemeine Ausnahmebehandlung für Exception kann Programmfehler verbergen.
  • Ordne eine unbekannte Ausnahme anhand ihrer Oberklassenkette ein.
2

Behandle Ausnahmen mit try, catch und finally

Die Behandlung eines Fehlers verändert, welche Anweisungen als Nächstes ausgeführt werden. Eine nützliche Ausnahmebehandlung muss zur Ausnahme passen und das Programm so hinterlassen, dass es fortfahren kann. Aufräumarbeiten müssen auch auf Pfaden ausgeführt werden, die vorzeitig verlassen werden. Verfolge Umwandlungen und Schleifen Schritt für Schritt, wähle spezifische Ausnahmebehandlungen vor allgemeinen und lege den geschützten Block um die Arbeit, die gemeinsam fehlschlagen soll. Verfolge dann, wie sich finally verhält, wenn eine Ausnahme weitergegeben wird oder eine Methode zurückkehrt.

2.1 Setze das Programm nach einer fehlgeschlagenen Umwandlung fort

Ein try-Block markiert Arbeit, die fehlschlagen kann. Ein passender catch-Block gibt dem Programm eine sinnvolle nächste Handlung vor, etwa nach einem anderen Wert zu fragen. Das Behandeln der Ausnahme wiederholt die fehlgeschlagene Anweisung nicht von selbst.

Verfolge die Zweige
System.out.println("A");
try {
    System.out.println("B");
    int value = Integer.parseInt("4x");
    System.out.println("C");
} catch (NumberFormatException e) {
    System.out.println("D: " + e.getMessage());
}
System.out.println("E");

Die Ausgabe besteht aus A, B, D: For input string: "4x" und E, jeweils in einer eigenen Zeile. Die Umwandlung schlägt fehl, daher überspringt Java C und führt die passende Ausnahmebehandlung aus. Endet diese normal, wird die Ausführung nach der gesamten Anweisung fortgesetzt. Gelingt die Umwandlung, wird die Ausnahmebehandlung übersprungen. Wirft der Block eine ArithmeticException, passt diese Ausnahmebehandlung nicht; die Ausnahme wird weitergegeben.

Wiederhole den Versuch mit neuer Eingabe

Lies vor der Umwandlung eine ganze Zeile. Die folgende Schleife akzeptiert eine Ganzzahl und behandelt das Ende der Tastatureingabe, indem sie meldet, dass kein Wert eingegeben wurde:

import java.util.Scanner;

class WholeNumbers {
    static int readWholeNumber(Scanner in) {
        while (in.hasNextLine()) {
            String line = in.nextLine();
            try {
                return Integer.parseInt(line.trim());
            } catch (NumberFormatException e) {
                System.out.println("Please enter a whole number.");
            }
        }
        throw new IllegalStateException("No number supplied");
    }
}

Bei den Eingaben „twenty“ und „20“ gibt die Ausnahmebehandlung einmal eine Meldung aus, die Schleife liest eine neue Zeile und die Methode gibt 20 zurück. trim() entfernt umgebenden Leerraum: Integer.parseInt(" 20") schlägt andernfalls fehl. Die Umwandlung akzeptiert ein optionales Vorzeichen und Ziffern, die eine Ganzzahl im Wertebereich darstellen.

Ein tokenbasierter Scanner benötigt eine andere Lösung. Trifft nextInt() auf „ten“, lässt die InputMismatchException dieses Token ungelesen. Wird die Ausnahme abgefangen und nextInt() erneut aufgerufen, wiederholt sich der Fehler endlos. Lies das fehlerhafte Token mit next() ein oder verwirf die Zeile mit nextLine(), bevor du es erneut versuchst. hasNextInt() kann ein Token vor der Umwandlung prüfen.

Halte den Zustand nach der Behandlung verwendbar

Eine innerhalb von try deklarierte Variable existiert nur in diesem Block. Benötigt späterer Code das Ergebnis, deklariere sie außerhalb und weise ihr auf jedem Pfad, der fortgesetzt wird, einen gültigen Wert zu:

int quantity;
try {
    quantity = Integer.parseInt("bad");
} catch (NumberFormatException e) {
    quantity = 0;
}
System.out.println(quantity);

Null ist hier nur dann angemessen, wenn das Programm sie als gültigen Ersatzwert definiert. Bleibt quantity in der Ausnahmebehandlung ohne Zuweisung, entsteht beim Kompilieren ein Fehler wegen fehlender eindeutiger Zuweisung.

Fange den spezifischen Typ ab, den du behandeln kannst. Eine leere Ausnahmebehandlung verschweigt das Problem; ein allgemeines catch (Exception e) kann einen Nullreferenzfehler als fehlerhafte Zahl ausgeben. Eine sinnvolle Fortsetzung sollte erneut fragen, ein Element überspringen, einen gültigen Standardwert wählen, den Fehler melden oder eine aussagekräftige Ausnahme weitergeben. Prüfe bei einfachen vorhersehbaren Grenzen zuerst: index >= 0 && index < list.size() verhindert sowohl negative als auch zu große Listenindizes.

Wichtige Erkenntnisse

  • Eine passende Ausnahmebehandlung wird ausgeführt, nachdem der Rest des fehlschlagenden try-Blocks übersprungen wurde.
  • Eine Schleife muss vor einem erneuten Versuch eine neue Eingabe lesen.
  • Scanner.nextInt() lässt ein nicht passendes Token ungelesen.
  • Jeder Pfad, der fortgesetzt wird, muss später benötigte Variablen sinnvoll initialisiert hinterlassen.

2.2 Wähle Ausnahmebehandlungen und die Arbeit, die sie schützen

Eine Datei kann fehlen, nicht lesbar oder voller fehlerhaft formatierter Datensätze sein. Diese Fehler erfordern unterschiedliche Reaktionen. Mehrere Ausnahmebehandlungen erlauben es einem try, sie zu unterscheiden, während die Position des Blocks entscheidet, wie viel Arbeit bei einem Fehler abgebrochen wird.

Setze spezifische Ausnahmebehandlungen zuerst

Java prüft Ausnahmebehandlungen von oben nach unten und führt die erste passende aus. Es führt für dieselbe Ausnahme keine späteren Ausnahmebehandlungen mehr aus.

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;

class FirstCount {
    static void show(Path file) {
        try {
            int count = Integer.parseInt(Files.readString(file).trim());
            System.out.println(count);
        } catch (NoSuchFileException e) {
            System.out.println("File not found: " + file.toAbsolutePath());
        } catch (IOException e) {
            System.out.println("Could not read: " + file.toAbsolutePath());
        } catch (NumberFormatException e) {
            System.out.println("The file must contain one whole number.");
        }
    }
}

NoSuchFileException erweitert IOException und muss daher zuerst kommen. Eine umgekehrte Reihenfolge macht die spezifische Ausnahmebehandlung unerreichbar und verhindert die Kompilierung. Die Umwandlungsausnahme ist nicht durch Vererbung mit IOException verbunden, daher können diese beiden Ausnahmebehandlungen in beliebiger Reihenfolge stehen.

Eine Ausnahmebehandlung für eine geprüfte Ausnahme muss außerdem für die geschützte Arbeit relevant sein. Wird IOException nur um int count = 5; herum abgefangen, wird das abgelehnt, weil dieser Block die Ausnahme nicht werfen kann. Diese Einschränkung gilt nicht in gleicher Weise für ungeprüfte Typen oder die allgemeinen Typen Exception und Throwable.

Verwende eine gemeinsame Ausnahmebehandlung für dieselbe Reaktion

Ein Multi-Catch verbindet nicht durch Vererbung zusammenhängende Ausnahmetypen mit |:

try {
    String[] parts = "Ana".split(",");
    int age = Integer.parseInt(parts[1]);
    System.out.println(age);
} catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
    System.out.println("Invalid record: " + e.getMessage());
}

Hier löst das fehlende zweite Feld die Indexausnahme aus. Ein vorhandenes, aber nicht numerisches Feld würde die Umwandlungsausnahme auslösen, und beide würden dieselbe Reaktion erhalten. Der Multi-Catch-Parameter ist implizit final. Die Alternativen dürfen nicht eine Unterklasse und ihre Oberklasse sein: FileNotFoundException | IOException ist ungültig, weil IOException bereits beide abdeckt.

Entscheide, was bei einem Fehler übersprungen werden soll

Betrachte die Texte „3“, „x“ und „5“. Liegt ein try um die gesamte Schleife, stoppt die Verarbeitung bei „x“ und die bisherige Summe ist 3. Setze das try in die Schleife, um jedes Element einzeln zu behandeln:

int sum = 0;
for (String text : new String[] {"3", "x", "5"}) {
    try {
        sum += Integer.parseInt(text);
    } catch (NumberFormatException e) {
        System.out.println("Skipped: " + text);
    }
}
System.out.println(sum);

Dies gibt Skipped: x und dann 8 aus. Die Schleife läuft weiter, weil ihr Rumpf den Fehler dieses Datensatzes behandelt hat. Bei verschachtelten Ausnahmebehandlungen beginnt die Suche im innersten geschützten Block und verläuft nach außen; eine nicht passende innere Ausnahmebehandlung lässt einer äußeren eine Chance. Wähle die kleinste Arbeitseinheit, die gemeinsam fehlschlagen soll: einen Datensatz bei fehlerhaften Daten, den Dateivorgang bei einem Lesefehler.

Wichtige Erkenntnisse

  • Java führt für jede Ausnahme höchstens eine passende Ausnahmebehandlung aus.
  • Fange eine Unterklasse vor ihrer Oberklasse ab.
  • Multi-Catch verwendet dieselbe Reaktion für nicht durch Vererbung zusammenhängende Ausnahmetypen.
  • Multi-Catch kann eine Oberklasse nicht mit ihrer Unterklasse verbinden.
  • Setze die Behandlung von Datensatzfehlern in die Schleife, um mit späteren Datensätzen fortzufahren.

2.3 Führe Aufräumarbeiten aus, bevor der Kontrollfluss den Block verlässt

Manche Arbeit muss erledigt werden, ob eine Operation gelingt oder fehlschlägt. Ein finally-Block wird ausgeführt, wenn der Kontrollfluss seine try-Anweisung verlässt, auch wenn eine Ausnahme weitergegeben wird oder eine Methode zurückkehrt.

Verfolge eine nicht passende Ausnahme
try {
    System.out.println("1");
    int[] values = new int[2];
    values[2] = 7;
    System.out.println("2");
} catch (NumberFormatException e) {
    System.out.println("3");
} finally {
    System.out.println("4");
}
System.out.println("5");

Nur 1 und 4 werden ausgegeben, bevor die ArrayIndexOutOfBoundsException weitergegeben wird. Index 2 liegt außerhalb des Arrays mit zwei Elementen. Die Ausnahmebehandlung passt nicht, aber finally wird trotzdem ausgeführt. Die Anweisung, die 5 ausgibt, steht außerhalb des Aufräumblocks und wird daher durch die weitergegebene Ausnahme übersprungen.

Wirft die geschützte Arbeit nach der Ausgabe von 1 stattdessen eine passende NumberFormatException, gibt die Ausnahmebehandlung 3 aus, die Aufräumarbeiten geben 4 aus und die Ausführung wird bei 5 fortgesetzt. Auf einem erfolgreichen Pfad wird keine Ausnahmebehandlung ausgeführt, die Aufräumarbeiten aber trotzdem.

Rückgabewerte werden vor den Aufräumarbeiten vorbereitet
class Cleanup {
    static int result() {
        try {
            return 1;
        } finally {
            System.out.println("cleanup");
        }
    }
}

Der Rückgabeausdruck wird zuerst ausgewertet. Java gibt dann cleanup aus und der Aufrufer erhält 1. Auch ein break oder continue, das einen geschützten Bereich verlässt, führt dessen Aufräumarbeiten aus, bevor die Änderung des Kontrollflusses wirksam wird.

Verwende kein return in finally. Ein dortiges return 2; würde den ausstehenden Rückgabewert ersetzen und könnte sogar eine ausstehende Ausnahme verwerfen. Eine bei den Aufräumarbeiten geworfene Ausnahme kann ebenso den ursprünglichen Fehler ersetzen. Die Aufräumarbeiten sollten kein zweites konkurrierendes Ergebnis erzeugen. Die Compileroption -Xlint:finally warnt, wenn ein finally-Block nicht normal enden kann.

Schließe ein Objekt nur, wenn es geöffnet wurde

Das ältere manuelle Muster benötigt eine Variable, die für den Aufräumblock sichtbar ist:

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Scanner;

class ManualRead {
    static void firstLine(Path file) throws IOException {
        Scanner sc = null;
        try {
            sc = new Scanner(file, StandardCharsets.UTF_8);
            if (sc.hasNextLine()) System.out.println(sc.nextLine());
            if (sc.ioException() != null) throw sc.ioException();
        } finally {
            if (sc != null) sc.close();
        }
    }
}

Schlägt das Öffnen fehl, ist sc weiterhin null, sodass die Prüfung verhindert, dass die Aufräumarbeiten eine neue Nullreferenzausnahme werfen. Für schließbare Objekte erspart try-with-resources diese Verwaltungsarbeit und geht sicherer mit konkurrierenden Fehlern um.

finally ist keine Garantie gegen das Anhalten der Maschine. System.exit beendet die JVM, ohne diese Aufräumarbeiten auszuführen; ein Absturz oder Stromausfall verhindert sie ebenfalls. Eine Endlosschleife, die den Block nie verlässt, erreicht auch dessen finally nie.

Wichtige Erkenntnisse

  • finally wird bei normalem Abschluss, abgefangenen und nicht abgefangenen Ausnahmen sowie Kontrollflussänderungen wie return ausgeführt.
  • Ein Rückgabeausdruck wird ausgewertet, bevor die Aufräumarbeiten ausgeführt werden.
  • Code nach einer try-Anweisung wird übersprungen, wenn eine Ausnahme weitergegeben wird; finally wird dennoch erreicht.
  • Eine Rückkehr oder geworfene Ausnahme in finally kann das ursprüngliche Ergebnis verbergen.
  • Try-with-resources ist die übliche Wahl zum Schließen von Dateiressourcen.
  • Das Beenden der JVM oder ein Block, der nie endet, kann Aufräumarbeiten verhindern.
3

Wirf Ausnahmen und entwirf eigene Ausnahmetypen

Eine ungültige Anfrage lässt sich am einfachsten diagnostizieren, bevor sie ein Objekt verändert. Deine Methoden können fehlerhafte Argumente oder Zustände mit Standardausnahmen ablehnen und Probleme aus dem Anwendungsbereich durch eigene Klassen kenntlich machen. Die Methodendeklaration informiert Aufrufer über geprüfte Ausnahmen; die Vererbung begrenzt, was eine Überschreibung hinzufügen darf. Wenn du einen tieferliegenden Fehler in einen nützlicheren umwandelst, erhalte seine Ursache, damit der Aufrufer sowohl Kontext als auch die ursprünglichen Diagnosehinweise erhält.

3.1 Signalisiere ungültige Argumente und Zustände

Deine Methoden können ungültige Anfragen ablehnen, bevor sie ein Objekt verändern. Wähle eine Ausnahme, die das Problem benennt und dem Aufrufer eine Reaktion ermöglicht.

Wirf jetzt ein Objekt
class Percentage {
    private int value;

    public void setPercentage(int value) {
        if (value < 0 || value > 100) {
            throw new IllegalArgumentException(
                "percentage must be 0 to 100, was " + value);
        }
        this.value = value;
    }
}

Der Aufruf setPercentage(120) wirft eine Ausnahme mit der Meldung percentage must be 0 to 100, was 120. Die Zuweisung wurde noch nicht ausgeführt, sodass das Feld seinen bisherigen Wert behält. Das ist das Fail-fast-Prinzip: Melde die ungültige Anfrage an der Stelle, an der du sie erklären kannst. Dieselbe Prüfung in einem Konstruktor verhindert, dass ein ungültiges Objekt zurückgegeben wird.

throw benötigt ein Ausnahmeobjekt, wie in throw new IllegalArgumentException("bad quantity");. Ein Klassenname allein ist kein Objekt. Eine gewöhnliche Anweisung direkt nach einem bedingungslosen throw im selben Block ist unerreichbar.

ProblemStandardausnahme
Argument hat einen ungültigen WertIllegalArgumentException
Objekt befindet sich für den Aufruf im falschen ZustandIllegalStateException
Benötigte Objektreferenz ist nullNullPointerException
Operation wird von der Klasse nicht angebotenUnsupportedOperationException
Position liegt außerhalb einer CollectionIndexOutOfBoundsException

Objects.requireNonNull(name, "name must not be null") aus java.util.Objects ist eine praktische null-Prüfung. Wenn eine bereits versandte Bestellung einen weiteren Artikel ablehnt, ist das ein Zustandsproblem; eine negative Artikelmenge ist ein Argumentproblem.

Deklariere, was die Methode verlassen kann

Eine Hilfsmethode zum Lesen von Dateien kann ihre geprüfte Ausnahme deklarieren:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

class NameLoader {
    static List<String> loadNames(Path file) throws IOException {
        return Files.readAllLines(file);
    }
}

throw ist eine ausführbare Anweisung. throws gehört zur Methodendeklaration und teilt Aufrufern mit, um welche geprüften Ausnahmen sie sich kümmern müssen. Es fängt weder einen Fehler ab noch sorgt es dafür, dass die Methode danach fortfährt. Jeder Aufrufer muss IOException abfangen oder seinerseits deklarieren. Ein catch-Block kann dasselbe Objekt auch mit throw e; erneut werfen.

Fange eine Ausnahme dort ab, wo eine sinnvolle Reaktion möglich ist. Eine Hilfsmethode kann den Benutzer normalerweise nicht auffordern, eine andere Datei zu wählen, main dagegen schon. Würde sie „Fehler“ ausgeben und eine leere Liste zurückgeben, ließe sich eine fehlende Datei nicht von einer tatsächlich leeren unterscheiden. Bei einem kleinen Experiment kann main mit throws IOException deklariert werden; ein fertiges Programm behandelt erwartete Fehler mit hilfreichen Meldungen. Ungeprüfte Typen dürfen zur Dokumentation deklariert werden, obwohl der Compiler es nicht verlangt.

Beachte den geerbten Vertrag

Deklariert ein Interface read() throws IOException, darf eine überschreibende Methode IOException, eine spezifischere geprüfte Unterklasse oder keine geprüfte Ausnahme deklarieren. Sie darf die Deklaration nicht auf Exception erweitern. Ungeprüfte Typen darf sie deklarieren. Eine Überschreibung von Object.toString() kann keine IOException hinzufügen, da die geerbte Methode keine geprüften Ausnahmen deklariert. Bei Aufrufen über den Interface-Typ muss die deklarierte IOException weiterhin behandelt werden, selbst wenn eine Implementierung keine deklariert.

Wichtige Erkenntnisse

  • Verwende throw mit einem Ausnahmeobjekt, um eine ungültige Operation abzubrechen.
  • Prüfe die Eingaben, bevor du Felder oder Collections änderst.
  • throws deklariert einen möglichen Fehler; es behandelt ihn nicht.
  • Fange Ausnahmen dort ab, wo eine sinnvolle Fortsetzung möglich ist; gib den Fehler andernfalls an den Aufrufer weiter.
  • Überschreibende Methoden dürfen keine neuen oder allgemeineren geprüften Ausnahmen hinzufügen.

3.2 Beschreibe fachliche Fehler und erhalte ihre Ursachen

Eine Standardausnahme genügt oft. Erstelle eine eigene Klasse, wenn Aufrufer ein programmspezifisches Problem wie einen unzureichenden Lagerbestand oder einen fehlerhaft formatierten Datensatz erkennen und anders darauf reagieren müssen.

Wähle die Oberklasse bewusst

Das Erweitern von Exception erzeugt eine geprüfte Ausnahme. Das Erweitern von RuntimeException erzeugt eine ungeprüfte Ausnahme. Wähle die geprüfte Variante, wenn Aufrufer mit einem behebbaren fachlichen Fehler rechnen sollen; wähle die ungeprüfte Variante bei falscher Verwendung oder einem Programmierfehler.

class OutOfStockException extends Exception {
    public OutOfStockException(String message) {
        super(message);
    }
}

super(message) speichert die Meldung in der geerbten Ausnahmeimplementierung, sodass getMessage() sie zurückgibt. Der Klassenname endet üblicherweise auf Exception. Zusätzliche Felder und Getter können strukturierte Angaben wie angeforderte und verfügbare Mengen bereitstellen.

Lass den Lagerbestand nach einem Fehler unverändert
import java.util.HashMap;

class Inventory {
    private final HashMap<String, Integer> stock = new HashMap<>();

    Inventory() {
        stock.put("pens", 12);
    }

    void remove(String item, int quantity) throws OutOfStockException {
        if (quantity <= 0) {
            throw new IllegalArgumentException("quantity must be positive");
        }
        int available = stock.getOrDefault(item, 0);
        if (quantity > available) {
            throw new OutOfStockException("Requested " + quantity
                + " of " + item + ", only " + available + " in stock");
        }
        stock.put(item, available - quantity);
    }
}

Bei einem Anfangsbestand von 12 Stiften bleiben nach der Entnahme von 5 noch 7. Eine spätere Anfrage nach 10 wirft eine Ausnahme, und es bleiben 7, weil die Aktualisierung erst nach allen Prüfungen erfolgt. Der Aufrufer muss die geprüfte Lagerbestandsausnahme behandeln oder deklarieren; die Ausnahme für eine ungültige Menge ist ungeprüft. Ein Programm, das eine eigene Reaktion auf unbekannte Artikel benötigt, könnte eine verwandte UnknownItemException verwenden, wobei beide fachlichen Typen unter einer Basisklasse InventoryException stehen.

Verpacke das Problem, ohne es zu verlieren

Eine Methode zum Parsen kann einen tieferliegenden Umwandlungsfehler in einen Datensatzfehler umwandeln:

class InvalidRecordException extends Exception {
    public InvalidRecordException(String message) {
        super(message);
    }
    public InvalidRecordException(String message, Throwable cause) {
        super(message, cause);
    }
}

Innerhalb der Methode zum Parsen kann eine Ausnahmebehandlung Folgendes tun:

catch (NumberFormatException e) {
    throw new InvalidRecordException(
        "Line 3: quantity is not a number: x", e);
}

Die umschließende Ausnahme gibt dem Aufrufer eine aussagekräftige Kategorie und eine Zeilennummer. Ihr getCause() gibt die ursprüngliche NumberFormatException zurück und ein ausgegebener Stacktrace enthält weiterhin einen Abschnitt Caused by:. Wird eine neue Ausnahme nur mit der alten Meldung erzeugt, gehen das ursprüngliche Objekt und sein Stacktrace verloren.

java.io.UncheckedIOException ist eine vorgefertigte ungeprüfte Hülle speziell für IOException. Ihre Konstruktoren akzeptieren die Ursache und optional eine Meldung. Sie kann einen Dateifehler durch eine überschreibende Methode weitergeben, die keine geprüfte Ausnahme deklarieren darf. Das Verpacken ändert den Typ, den der Aufrufer sieht; es behebt das Dateiproblem nicht. Drücke gewöhnliche Schleifenentscheidungen mit Bedingungen und break aus, ohne für den normalen Ablauf Ausnahmen zu erfinden.

Wichtige Erkenntnisse

  • Eigene Klassen ermöglichen Aufrufern, aussagekräftige fachliche Fehler zu erkennen.
  • Erweitere Exception für eine geprüfte Klasse oder RuntimeException für eine ungeprüfte Klasse.
  • Rufe super(message) auf, um eine hilfreiche Diagnose zu erhalten.
  • Prüfe die gesamte Anfrage, bevor du den Zustand änderst.
  • Übergib beim Verpacken die ursprüngliche Ausnahme als Ursache.
  • UncheckedIOException gibt eine IOException als ungeprüfte Ausnahme weiter.
4

Bestimme Dateipfade und schließe Ressourcen sicher

Eine Textdatei muss gefunden, dekodiert und korrekt geschlossen werden. Ein relativer Pfad hängt vom Arbeitsverzeichnis ab, während Bytes erst durch die richtige Kodierung zu lesbaren Zeichen werden. Bei Zahlen als Text und Zeilenenden musst du eigene Entscheidungen für die Übertragbarkeit treffen. Sobald eine Datei geöffnet ist, gibt try-with-resources sie bei jedem Verlassen frei und hält den Hauptfehler sichtbar, falls auch das Schließen fehlschlägt. Diese Entscheidungen machen späteren Code zum Lesen und Schreiben verlässlicher.

4.1 Sorge dafür, dass Textdateien auf verschiedenen Computern funktionieren

Eine Datei speichert Bytes, während dein Programm mit Zeichen und Zahlen arbeitet. Für einen erfolgreichen Lesevorgang brauchst du den richtigen Speicherort, die richtige Zeichenkodierung und eine klare Konvention für Zeilen und Zahlen als Text.

Finde den tatsächlichen Speicherort

Ein relativer Pfad beginnt im Arbeitsverzeichnis des Programms. Er beginnt nicht im Ordner mit dem Quellcode. Ein absoluter Pfad beginnt im Wurzelverzeichnis des Dateisystems, etwa /home/ana/data/scores.txt, oder im Stammverzeichnis eines Laufwerks unter Windows.

Path file = Path.of("data", "scores.txt");
System.out.println(System.getProperty("user.dir"));
System.out.println(file.toAbsolutePath());
System.out.println(file.getFileName());

Importiere java.nio.file.Path. Das Erzeugen dieses Path öffnet nichts und prüft nicht, ob etwas existiert. toAbsolutePath() zeigt, wo Java suchen wird, und getFileName() liefert scores.txt. Eine IDE verwendet häufig den Projektordner als Arbeitsverzeichnis. Schrägstriche in "data/scores.txt" funktionieren unter Windows, Linux und macOS. In einem Windows-Pfadliteral mit umgekehrten Schrägstrichen müssen diese als Escape-Sequenz geschrieben werden, wie in "C:\\data\\scores.txt". Auch das ältere java.io.File stellt einen Speicherort dar; wandle mit toPath() oder toFile() um.

Ordne Bytes den Zeichen zu

UTF-8 kodiert jedes Unicode-Zeichen. Einfache lateinische Zeichen benötigen ein Byte; andere benötigen zwei bis vier. Der Text „café“ enthält vier Zeichen, aber fünf UTF-8-Bytes, weil „é“ zwei benötigt. Werden diese Bytes als ISO-8859-1 gelesen, kann „café“ entstehen. Verwende die Kodierung, mit der der Text geschrieben wurde.

Die Textmethoden von Files verwenden standardmäßig UTF-8. Klassen wie FileWriter und ein ohne Zeichensatz geöffneter Scanner verwenden den Standardzeichensatz, der seit Java 18 UTF-8 ist; bei älteren Installationen kann er abweichen. StandardCharsets.UTF_8 aus java.nio.charset legt die Wahl ausdrücklich fest. Ungültige Bytes beim Lesen mit Files.readAllLines erzeugen eine MalformedInputException, eine Unterklasse von IOException.

Lies Zeilen ohne zurückbleibende Zeilenenden
KonventionJava-Escape-Sequenz im String
Zeilenende unter Linux und macOS\n
Typisches Zeilenende unter Windows\r\n

Mit einem Abschlusszeichen nach jeder Zeile benötigen „a“ und „b“ vier Bytes mit LF-Enden und sechs mit CRLF-Enden. BufferedReader.readLine(), Files.readAllLines und Scanner.nextLine() entfernen erkannte Zeilenenden. Wird ein vollständiger String mit split("\n") aufgeteilt, bleibt das Windows-\r stehen, sodass die Umwandlung von "12\r" mit parseInt fehlschlägt. Bevorzuge Methoden zum zeilenweisen Lesen oder entferne den umgebenden Leerraum. System.lineSeparator() und der Formatspezifizierer %n erzeugen das Zeilenende des aktuellen Systems.

Halte Zahlen in Dateien einheitlich

Integer.parseInt akzeptiert Ganzzahlen als Text ohne Dezimaltrennzeichen. Double.parseDouble("3.5") akzeptiert einen Punkt; Double.parseDouble("3,5") schlägt fehl. Diese Methoden zum Parsen ignorieren das Gebietsschema. Numerische Scanner-Methoden und gewöhnliche Dezimalformatierung richten sich nach dem Gebietsschema, sodass deutsche oder niederländische Einstellungen „3,5“ erwarten und „3,50“ erzeugen können. Verwende scanner.useLocale(Locale.ROOT) und String.format(Locale.ROOT, "%.2f", value) für Daten mit Dezimalpunkt; importiere java.util.Locale.

Fehlende Speicherorte führen häufig zu einer NoSuchFileException bei Files oder Scanner(Path), während ältere Konstruktoren mit Dateinamen eine FileNotFoundException ergeben können. Fehlende Berechtigungen können zu einer AccessDeniedException führen. Dies sind geprüfte Ein- und Ausgabefehler: Ein korrekter Pfadausdruck allein kann sie nicht verhindern.

Wichtige Erkenntnisse

  • Relative Pfade werden vom Arbeitsverzeichnis aus aufgelöst.
  • Das Erzeugen eines Path öffnet oder prüft die Datei nicht.
  • Stimme die Kodierungen beim Lesen und Schreiben ab; wähle UTF-8 ausdrücklich, wo Standardwerte abweichen können.
  • Methoden zum zeilenweisen Lesen entfernen die Zeilenenden LF, CRLF und CR.
  • Double.parseDouble verwendet einen Punkt; gebietsschemaabhängige numerische Methoden benötigen ein gewähltes Gebietsschema.
  • Beim Öffnen und Lesen von Dateien müssen weiterhin geprüfte Ausnahmen behandelt werden.

4.2 Lass Java jede geöffnete Ressource schließen

Eine offene Datei belegt eine Ressource des Betriebssystems. Das Schließen gibt sie frei und leert den Puffer eines Writers mit ausstehender Ausgabe. Ein vergessenes Schließen kann die erlaubte Anzahl offener Dateien ausschöpfen, gepufferten Text verlieren lassen oder verhindern, dass ein anderes Programm die Datei ändert.

Deklariere Ressourcen im try-Kopf
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;

class Lines {
    static void show(Path file) throws IOException {
        try (BufferedReader in =
                Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
            String line;
            while ((line = in.readLine()) != null) {
                System.out.println(line);
            }
        }
    }
}

Der Kopf öffnet den Reader. Der Rumpf verwendet ihn und Java schließt ihn, wenn der Rumpf normal endet, zurückkehrt oder eine Ausnahme wirft. Ein eigenes finally ist nicht nötig. Hier gibt es keine Ausnahmebehandlung, sodass jede geprüfte Ausnahme weiterhin über throws IOException an den Aufrufer weitergegeben wird.

Eine Ressource muss AutoCloseable implementieren. Dieses Interface deklariert close() und erlaubt Exception; sein Unterinterface Closeable schränkt die deklarierte geprüfte Ausnahme auf IOException ein. Reader, Writer und Scanner erfüllen diese Bedingung. Ein String tut das nicht. Variablen im Kopf sind implizit final und stehen im Rumpf zur Verfügung, nicht aber in den catch- oder finally-Blöcken dieser Anweisung. Java 9 und neuer erlauben auch eine vorhandene finale oder effektiv finale Variable: try (in) { ... }.

Schließe in umgekehrter Reihenfolge

Trenne Ressourcen durch Semikolons:

try (BufferedReader in = Files.newBufferedReader(source);
     java.io.BufferedWriter out = Files.newBufferedWriter(target)) {
    String line;
    while ((line = in.readLine()) != null) {
        out.write(line);
        out.newLine();
    }
}

Dies ist ein Block innerhalb einer Methode, die IOException deklariert, mit unterschiedlichen Pfaden source und target. Java öffnet zuerst in, dann out und schließt out vor in. Schlägt das Öffnen von out fehl, wird der bereits geöffnete Reader trotzdem geschlossen. Ein Writer für den Eingabepfad würde diese Eingabe beim Öffnen leeren, daher müssen die Pfade für diese direkte Kopie verschieden sein.

Ressourcen werden vor den catch- und finally-Klauseln derselben Anweisung geschlossen. Diese Ausnahmebehandlungen können Fehler beim Öffnen, Verwenden oder Schließen der Ressourcen erhalten.

Halte den Hauptfehler sichtbar

Angenommen, der Rumpf wirft eine Datensatzausnahme und anschließend wirft das Schließen des Readers eine IOException. Try-with-resources gibt die Ausnahme aus dem Rumpf weiter und fügt den Fehler beim Schließen als unterdrückt hinzu. getSuppressed() ruft ihn ab und der Stacktrace gibt einen Abschnitt Suppressed: aus. Der ursprüngliche Fehler aus dem Rumpf bleibt sichtbar. Ist der Rumpf erfolgreich und schlägt nur das Schließen fehl, wird die Ausnahme beim Schließen normal weitergegeben.

Bei mehreren Fehlern während des Schließens versucht Java weiterhin, die übrigen Ressourcen zu schließen. Ein einfaches finally { resource.close(); } kann dagegen einen ausstehenden Fehler aus dem Rumpf durch einen Fehler beim Aufräumen ersetzen.

Verwende dieses Muster für Dateiressourcen, die deinem Code gehören. Das Schließen eines Scanners für System.in schließt auch die Tastatureingabe des Programms und verhindert spätere Lesezugriffe. Die Verantwortung für die gemeinsam genutzte Standardeingabe sollte beim Programm liegen, statt sie in einer Hilfsmethode zu schließen.

Wichtige Erkenntnisse

  • Try-with-resources schließt erfolgreich geöffnete Ressourcen beim normalen Verlassen und beim Verlassen aufgrund einer Ausnahme.
  • Nur AutoCloseable-Objekte eignen sich als Ressourcen.
  • Ressourcen werden von links nach rechts geöffnet und von rechts nach links geschlossen.
  • Das Schließen erfolgt vor den catch- und finally-Blöcken derselben Anweisung.
  • Ein Fehler im Rumpf bleibt vorrangig; spätere Fehler beim Schließen werden unterdrückt.
  • Das automatische Schließen ersetzt nicht die Behandlung nach der Catch-or-declare-Regel.
  • Das Schließen eines Scanners schließt auch seine schließbare Eingabequelle.
5

Lies und schreibe Textdateien

Die Dateiverarbeitung verbindet externe Bytes mit den Objekten und Ergebnissen, die dein Programm verwendet. Lies Zeilen mit einer zur Eingabegröße passenden Methode, prüfe Felder vor der Umwandlung und entscheide beim Schreiben bewusst zwischen Ersetzen und Anhängen. Ein vollständiger Bericht benötigt unterschiedliche Reaktionen auf einen fehlerhaften Datensatz und eine nicht verwendbare Datei. Erstelle das Ergebnis vor dem Speichern und verwende anschließend eine temporäre Ausgabedatei und ein unterstütztes atomares Verschieben, wenn ein vorhandenes Ziel geschützt werden muss.

5.1 Lies Zeilen und wandle sie in Objekte um

Das Lesen liefert dir Text. Das Parsen gibt diesem Text eine Bedeutung: eine Anzahl, ein Alter oder ein Objekt. Halte diese beiden Vorgänge getrennt, damit ein fehlerhafter Datensatz nicht wie ein fehlgeschlagenes Lesen der Datei aussieht.

Entscheide, wie viel im Speicher bleiben soll
MethodeErgebnis und Verwendung
Files.readAllLines(path)Eine Liste mit jeder Zeile; praktisch für kleine Dateien
Files.readString(path)Ein String, der Zeilenenden enthält
BufferedReader.readLine()Jeweils eine Zeile; null signalisiert das Ende
Scanner.hasNextLine() / nextLine()Praktisches zeilenweises Lesen

Importiere java.nio.file.Files und Path sowie die verwendeten Reader- oder Collection-Typen. Die Textmethoden von Files verwenden standardmäßig UTF-8. Ein BufferedReader gehört in try-with-resources und meldet Lesefehler mit IOException. Das Lesen einzelner Zeilen vermeidet es, die gesamte Eingabe im Speicher zu halten, sofern die Verarbeitung nicht anschließend alle Datensätze speichert.

Auch ein Scanner muss geschlossen werden und benötigt eine zusätzliche Fehlerprüfung:

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Scanner;

class ScanFile {
    static void show(Path file) throws IOException {
        try (Scanner sc = new Scanner(file, StandardCharsets.UTF_8)) {
            while (sc.hasNextLine()) {
                System.out.println(sc.nextLine());
            }
            if (sc.ioException() != null) throw sc.ioException();
        }
    }
}

Das Öffnen kann direkt eine Ausnahme werfen. Während des Lesens behandelt Scanner einen zugrunde liegenden Ein- oder Ausgabefehler als Ende der Eingabe und speichert ihn; ioException() macht ihn zugänglich. Ohne diese abschließende Prüfung kann ein unvollständiger Lesevorgang erfolgreich erscheinen.

Zähle die tatsächlichen Zeilen und parse sie dann

Erhöhe bei den Eingabezeilen „12“, „15“, „abc“, einer leeren Zeile und „9“ die Zeilennummer, bevor du entscheidest, ob du eine leere Zeile überspringst. Wandle jede nicht leere Zeile innerhalb eines eigenen try mit Integer.parseInt(line.trim()) um. Melde „Line 3: not a number: abc“ und fahre fort. Die gültige Summe ist 36 und die Anzahl 3, was den Mittelwert 12,0 ergibt. Berechne (double) sum / count, um einen möglichen Nachkommateil zu erhalten, und prüfe zuerst, dass die Anzahl nicht null ist.

Prüfe Felder vor dem Zugriff über Indizes

Einfache kommagetrennte Daten können zu Objekten werden. Hier ist die erste Zeile eine erforderliche Kopfzeile; jeder weitere Datensatz hat einen nicht leeren Namen und ein ganzzahliges Alter:

String[] parts = line.split(",", -1);
if (parts.length != 2 || parts[0].isBlank() || parts[1].isBlank()) {
    System.out.println("Line " + lineNumber + ": expected name and age");
    continue;
}
try {
    int age = Integer.parseInt(parts[1].trim());
    people.add(new Person(parts[0].trim(), age));
} catch (NumberFormatException e) {
    System.out.println("Line " + lineNumber + ": invalid age");
}

Dieses Fragment eines Schleifenrumpfs setzt line, lineNumber, eine List<Person> namens people und einen Konstruktor Person(String, int) voraus. Überspringe die Kopfzeile „name,age“ vor diesem Parsen; andernfalls schlägt die numerische Umwandlung von „age“ fehl. Die Datensätze „Ana,34“, „Ben,29“ und „Chen,41“ ergeben zusammen 104 Jahre und im Durchschnitt etwa 34,67.

Ein einfaches split(",") verwirft leere Felder am Ende: „Eve,“ ergibt nur einen Teil. Die Begrenzung -1 erhält das leere zweite Feld, sodass die Validierung es melden kann. Prüfe immer die Länge vor parts[1]. Das Aufteilen allein verarbeitet keine Kommas innerhalb von Anführungszeichen, etwa im Namen in „"Smith, Ana",34“; vollständiges CSV benötigt einen CSV-Parser.

Wichtige Erkenntnisse

  • Wähle Methoden für die gesamte Datei bei überschaubaren Eingaben oder eine Reader-Schleife bei großen Eingaben.
  • Prüfe nach dem Lesen Scanner.ioException(), um einen verborgenen Lesefehler zu erkennen.
  • Zähle die ursprünglichen Dateizeilen auch dann, wenn leere Zeilen oder Datensätze übersprungen werden.
  • Prüfe Feldanzahl und erforderliche Werte vor dem Indexzugriff und der Umwandlung.
  • Überspringe eine Kopfzeile, bevor du Datensätze parst.
  • Einfaches Aufteilen kann CSV-Felder in Anführungszeichen nicht parsen.

5.2 Schreibe vollständigen Text und hänge bewusst an

Das Öffnen einer Ausgabedatei kann sie sofort verändern. Entscheide dich vor dem Öffnen des Writers für Ersetzen oder Anhängen und schließe ihn anschließend, damit gepufferte Ausgabe nicht im Speicher zurückbleibt.

Ersetzen oder anhängen

Files.writeString(path, text) schreibt einen String. Files.write(path, lines) schreibt ein durchlaufbares Objekt von Zeilen und fügt nach jeder Zeile, auch der letzten, das Zeilentrennzeichen des Systems ein. Mit ihren Standardoptionen erstellen beide eine fehlende Datei und leeren eine vorhandene.

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;

class RunLog {
    static void recordStart(Path log) throws IOException {
        Files.writeString(log, "started" + System.lineSeparator(),
            StandardCharsets.UTF_8,
            StandardOpenOption.CREATE, StandardOpenOption.APPEND);
    }
}

CREATE erlaubt eine neue Datei; APPEND erhält den alten Inhalt und fügt am Ende hinzu. APPEND allein schlägt fehl, wenn die Datei fehlt. Das Anhängen fügt vor deinem Text kein fehlendes Trennzeichen ein: Endet eine vorhandene Datei mitten in einer Zeile, setzen die nächsten Zeichen diese Zeile fort.

Der ältere FileWriter hat ein Flag zum Anhängen: new FileWriter("log.txt", StandardCharsets.UTF_8, true). Ohne Anhängen leert auch ein neuer FileWriter oder ein über den Dateinamen geöffneter PrintWriter eine vorhandene Datei. Öffne niemals einen Writer, der den Inhalt ersetzt, für eine Eingabe, die du noch lesen musst.

Wähle einen Writer und beachte Fehler

BufferedWriter unterstützt write und newLine() und meldet Schreibfehler als geprüfte Ausnahmen:

import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Locale;

class ReportWriter {
    static void write(Path file) throws IOException {
        try (BufferedWriter out =
                Files.newBufferedWriter(file, StandardCharsets.UTF_8)) {
            out.write("Readings: 3");
            out.newLine();
            out.write(String.format(Locale.ROOT, "Average: %.2f", 3.0));
            out.newLine();
        }
    }
}

Die Datei hat zwei Zeilen: „Readings: 3“ und „Average: 3.00“. Locale.ROOT legt den Dezimalpunkt fest. newLine(), println und %n verwenden alle das Zeilenende der Plattform; ein literales \n schreibt auf jedem System LF.

Ein PrintWriter bietet die praktischen Methoden print, println und printf, aber diese Methoden speichern Ein- und Ausgabefehler, statt sie zu werfen. checkError() leert den Puffer und gibt das Fehlerflag zurück. Wenn du auch Fehler beim abschließenden Schließen prüfen willst, behalte den Zugriff auf den Writer und prüfe nach dem Schließen. Für Code, der Schreibfehler zuverlässig weitergeben muss, verwende BufferedWriter oder die Files-Methoden.

Vervollständige den Weg zurück zum Lesen

Writer puffern die Ausgabe. flush() sendet ausstehenden Text weiter; close() leert den Puffer und gibt die Datei frei. Try-with-resources stellt sicher, dass bei jedem Verlassen versucht wird zu schließen, kann aber einen erfolgreichen Schreibvorgang auf die Festplatte nicht garantieren.

Um eine Person zu speichern, schreibe den Namen, ein Komma und das ganzzahlige Alter und dann ein Zeilenende. Formatiere Dezimalzahlen mit Locale.ROOT, damit ein späteres Double.parseDouble sie lesen kann. Ein fehlendes übergeordnetes Verzeichnis wird beim Öffnen einer Datei nicht erstellt: Verwende Files.createDirectories(folder), wenn es erstellt werden soll, oder melde den Fehler. Ein Schreibvorgang kann nach dem Leeren der Datei oder einer teilweisen Ausgabe fehlschlagen; verwende eine temporäre Ausgabedatei und atomares Verschieben, wenn das Ziel geschützt werden muss.

Wichtige Erkenntnisse

  • Standardmäßiges Schreiben erstellt eine fehlende Datei und leert eine vorhandene.
  • Verwende CREATE mit APPEND, um alten Inhalt zu erhalten und eine neue Protokolldatei zu ermöglichen.
  • Das Schließen leert den Ausgabepuffer und gibt die Ressource frei.
  • PrintWriter speichert Schreibfehler; checkError() macht sie zugänglich.
  • BufferedWriter und Files-Methoden geben Ein- und Ausgabefehler weiter.
  • Verwende UTF-8 und Locale.ROOT, damit sich Text übertragbar schreiben und wieder lesen lässt.
  • Das Schreiben einer Datei erstellt keine fehlenden übergeordneten Ordner.

5.3 Erstelle einen Bericht, ohne Fehler zu verbergen

Ein robustes Programm zur Dateiverarbeitung benötigt zwei Ebenen der Fehlerbehandlung. Ein fehlerhafter Datensatz kann übersprungen und gemeldet werden; ein Fehler beim Lesen oder Speichern der Datei stoppt die Aufgabe. Berechne ein vollständiges Ergebnis, bevor du auf das Ziel zugreifst.

Lesen, prüfen, berechnen, speichern

Das folgende vollständige Programm liest die optionale Kopfzeile „date,temperature“, gefolgt von einfachen Datensätzen mit zwei Feldern. Datumsangaben sind nicht leere Bezeichnungen; Temperaturen sind endliche Zahlen in Grad Celsius. Es überspringt und meldet fehlerhaft formatierte Datensätze einschließlich leerer Zeilen. Die verschachtelte geprüfte Ausnahme hält Datensatzprobleme von Dateiproblemen getrennt.

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.List;
import java.util.Locale;

public class TemperatureReport {
    static class InvalidRecordException extends Exception {
        InvalidRecordException(String message) { super(message); }
        InvalidRecordException(String message, Throwable cause) {
            super(message, cause);
        }
    }

    static List<String> readLines(Path file) throws IOException {
        return Files.readAllLines(file, StandardCharsets.UTF_8);
    }

    static double parseTemperature(String line, int lineNumber)
            throws InvalidRecordException {
        String[] parts = line.split(",", -1);
        if (parts.length != 2 || parts[0].isBlank()
                || parts[1].isBlank()) {
            throw new InvalidRecordException("Line " + lineNumber
                + ": expected a date label and a temperature");
        }
        try {
            double value = Double.parseDouble(parts[1].trim());
            if (!Double.isFinite(value)) {
                throw new InvalidRecordException("Line " + lineNumber
                    + ": temperature must be finite");
            }
            return value;
        } catch (NumberFormatException e) {
            throw new InvalidRecordException("Line " + lineNumber
                + ": not a number: " + parts[1], e);
        }
    }

    static String summarize(List<String> lines) {
        int count = 0;
        int skipped = 0;
        double sum = 0;
        double min = Double.POSITIVE_INFINITY;
        double max = Double.NEGATIVE_INFINITY;
        for (int i = 0; i < lines.size(); i++) {
            String line = lines.get(i);
            if (i == 0 && line.equals("date,temperature")) continue;
            try {
                double value = parseTemperature(line, i + 1);
                sum += value;
                count++;
                min = Math.min(min, value);
                max = Math.max(max, value);
            } catch (InvalidRecordException e) {
                skipped++;
                System.err.println(e.getMessage());
            }
        }
        if (count == 0) {
            return String.format(Locale.ROOT,
                "No valid readings%nSkipped lines: %d%n", skipped);
        }
        return String.format(Locale.ROOT,
            "Valid readings: %d%nAverage (°C): %.2f%n"
                + "Minimum (°C): %.2f%nMaximum (°C): %.2f%n"
                + "Skipped lines: %d%n",
            count, sum / count, min, max, skipped);
    }

    static void writeSummary(Path file, String text) throws IOException {
        Path target = file.toAbsolutePath();
        Path temp = Files.createTempFile(target.getParent(), "report-", ".tmp");
        try {
            Files.writeString(temp, text, StandardCharsets.UTF_8);
            Files.move(temp, target, StandardCopyOption.ATOMIC_MOVE);
        } catch (IOException e) {
            try {
                Files.deleteIfExists(temp);
            } catch (IOException cleanup) {
                e.addSuppressed(cleanup);
            }
            throw e;
        }
    }

    public static void main(String[] args) {
        Path input = Path.of("readings.csv");
        Path output = Path.of("summary.txt");
        try {
            List<String> lines = readLines(input);
            String report = summarize(lines);
            writeSummary(output, report);
            System.out.println("Saved: " + output.toAbsolutePath());
        } catch (NoSuchFileException e) {
            System.err.println("File or folder not found: " + e.getFile());
        } catch (AtomicMoveNotSupportedException e) {
            System.err.println("Could not save atomically: "
                + output.toAbsolutePath());
        } catch (IOException e) {
            System.err.println("Could not process " + input.toAbsolutePath()
                + " or save " + output.toAbsolutePath() + ": " + e.getMessage());
        }
    }
}
Verfolge einen Programmlauf

Verwende diese fünf Eingabezeilen ohne Kopfzeile:

2026-03-01,4.5
2026-03-02,6.0
2026-03-03,abc
2026-03-04,-1.5
missing comma

Die Zeilen 1, 2 und 4 ergeben drei gültige Messwerte. Die Summe ist 9,0; der Mittelwert beträgt 3,00 °C, das Minimum -1,50 °C und das Maximum 6,00 °C. Die Zeilen 3 und 5 erzeugen Diagnosemeldungen und der Bericht verzeichnet zwei übersprungene Zeilen. Die Ausnahmebehandlung beim Parsen liegt innerhalb der Schleife, sodass nach jedem fehlerhaften Datensatz spätere Datensätze erreichbar bleiben. Werden Summen erst nach erfolgreichem Parsen aktualisiert, bleiben verworfene Werte außen vor.

Double.parseDouble akzeptiert besondere Strings wie „NaN“ und „Infinity“. Double.isFinite lehnt sie hier ab, weil sie nicht als gewöhnliche gemessene Temperaturen dienen können. Bei einer leeren Datei, einer Datei nur mit Kopfzeile oder ausschließlich ungültigen Datensätzen wird der Zweig für die Anzahl null vor der Division oder Verwendung des anfänglichen Minimums und Maximums erreicht.

Schütze das Ziel

Das Lesen und die Berechnung sind abgeschlossen, bevor das Speichern beginnt. Files.writeString öffnet und schließt seine Datei selbst, kann aber fehlschlagen, nachdem ein Teil geschrieben wurde. Das Programm schreibt daher in eine temporäre Datei im Zielordner, schließt sie und fordert ATOMIC_MOVE an. Das Verschieben macht das fertige Ergebnis, sofern unterstützt, als eine einzige Dateisystemoperation sichtbar.

Das atomare Ersetzen eines vorhandenen Ziels hängt von der Implementierung ab. Das Programm meldet ein nicht unterstütztes Verschieben oder Ersetzen und versucht es nie erneut mit einem nicht atomaren Verschieben. Ein Fehler beim Aufräumen wird der ursprünglichen Ausnahme hinzugefügt, statt sie zu ersetzen. Atomare Sichtbarkeit garantiert nicht, dass die Daten einen Stromausfall überstehen.

Eine vorherige Prüfung von Files.exists(input) kann das spätere Lesen nicht garantieren: Eine Datei kann nach der Prüfung verschwinden oder unzugänglich werden. Behandle den tatsächlichen Vorgang. Bevor du dich auf ein Verarbeitungsprogramm verlässt, teste fehlende Eingaben, fehlende Ausgabeordner, leere Dateien und Dateien nur mit Kopfzeile, CRLF-Zeilenenden, Text mit Akzentzeichen und fehlerhaft formatierte Datensätze. Benutzermeldungen sollten den betroffenen Pfad nennen; für die Fehlersuche bei einer umschließenden Ausnahme werden auch ihre ursprüngliche Ursache und ihr Stacktrace benötigt.

Wichtige Erkenntnisse

  • Behandle Datensatzfehler innerhalb der Schleife und Dateifehler außerhalb.
  • Prüfe einen vollständigen Datensatz, bevor du Anzahlen oder Summen aktualisierst.
  • Prüfe vor der Berechnung eines Mittelwerts, ob die Anzahl gültiger Datensätze null ist.
  • Schließe Lesen und Berechnung ab, bevor du die Ausgabedatei öffnest.
  • Schreibe und schließe eine temporäre Datei und fordere dann ein atomares Verschieben an, um das Ziel zu schützen.
  • Melde nicht unterstütztes atomares Ersetzen, statt stillschweigend auszuweichen.
  • Halte ursprüngliche Ursachen und Fehler beim Aufräumen für die Diagnose verfügbar.

Ausnahmen und Dateien in Java: Beispiele

5 Interaktive Lektionen

1

Verfolge einen Fehler bis zu seiner Ursache zurück

11 Schritte
Lies einen Stacktrace, verbinde ihn mit den aktiven Aufrufen und ordne den Fehler anhand der Vererbung ein.
  1. 1
    EinführungEin Programm kann kompilieren und dennoch fehlschlagen

    Ein Eingabewert kann dazu führen, dass gültiger Java-Code während der Ausführung fehlschlägt. Das Ausnahmeobjekt zeigt dir, welche Art von Problem aufgetreten ist, und der Stacktrace zeigt, welche Methodenaufrufe aktiv waren.

    Verfolge zunächst eine fehlgeschlagene Ganzzahlumwandlung. Nutze dann dieselben Vererbungsregeln, durch die Objekte von Unterklassen als Objekte ihrer Oberklasse verwendbar sind, um zu entscheiden, was du laut Compiler behandeln musst.

  2. 2
    BeispielBeobachte, wie die Umwandlung abbricht

    Eine Methode loadCount führt diese Anweisungen aus:

    System.out.print("start ");
    int count = Integer.parseInt("eight");
    System.out.print("finish");
    

    Sie gibt start aus und wirft dann NumberFormatException. Die Zuweisung wird nicht abgeschlossen und finish wird übersprungen. Eine passende Ausnahmebehandlung kann übernehmen, aber die fehlgeschlagene Anweisung wird nach der Behandlung nicht fortgesetzt.

  3. 3
    ErklärungVerfolge die aktiven Aufrufe

    Angenommen, main hat loadCount aufgerufen, das wiederum Integer.parseInt aufgerufen hat. Diese Aufrufe bilden einen Teil des Aufrufstapels. Die Ausnahme wandert nach außen, wenn eine Methode keine passende Ausnahmebehandlung hat.

    parseInt schlägt zuerst fehl, loadCount kann seine normale Arbeit nicht abschließen und der Aufrufer erhält die Gelegenheit, den Fehler zu behandeln. Verlässt die Ausnahme main unbehandelt, meldet Java sie und der Hauptthread endet.

  4. 4
    BeispielFinde deinen ersten Stapelrahmen

    Ein Stacktrace kann nach den Stapelrahmen der Java-Bibliothek diese Anwendungsrahmen enthalten:

    at Counts.loadCount(Counts.java:14)
    at Counts.main(Counts.java:5)
    

    Untersuche zuerst Zeile 14: Das ist der Aufruf in deinem Code, der der Stelle am nächsten liegt, an der das Problem erkannt wurde. Zeile 5 zeigt dir, wo loadCount aufgerufen wurde. Die erste Zeile der Ausnahme nennt ihre Klasse und Meldung; verwechsle den darunterliegenden Stapelrahmen des Aufrufers nicht mit der Anweisung, die den fehlerhaften Text zuerst verwendet hat.

  5. 5
    HerausforderungFinde die übersprungene Ausgabe

    Eine Hilfsmethode gibt A aus, ruft Integer.parseInt("q2") auf und würde dann B ausgeben. Ihr Aufrufer fängt NumberFormatException ab und gibt C aus. Welche Buchstaben werden ausgegeben und warum?

    ---reveal---

    A und dann C. Die Umwandlung wirft vor B eine Ausnahme; durch die Weitergabe gelangt der Fehler zur passenden Ausnahmebehandlung des Aufrufers. Die übrigen Anweisungen der Hilfsmethode werden abgebrochen, sodass die Behandlung nicht zurückkehrt, um B auszugeben.

  6. 6
    TippSchau über die erkannte Anweisung hinaus

    Die Anweisung, in der ein Fehler erkannt wird, verwendet möglicherweise einen fehlerhaften Wert, der früher entstanden ist. Ein Nullreferenzfehler weist dich an, zurückzuverfolgen, wo diese Referenz ein Objekt hätte erhalten sollen.

    Verwende getMessage() für den Diagnosetext und printStackTrace() für die Aufrufkette. Eine Meldung kann null sein; ein Abschnitt Caused by: liefert eine weitere Ausnahme, die in einer Hülle erhalten blieb.

  7. 7
    ErklärungNutze die Oberklassenkette

    Throwable ist die Basisklasse für werfbare Objekte. Ihre Hauptzweige sind Error und Exception; RuntimeException steht unter Exception.

    RuntimeException, Error und jede Unterklasse einer der beiden sind ungeprüft. Andere Throwable-Unterklassen sind geprüft. Beide Kategorien treten während der Ausführung auf; „geprüft“ beschreibt die Prüfung der Behandlung durch den Compiler und keinen anderen Zeitpunkt, zu dem die Ausnahme geworfen wird.

  8. 8
    BeispielVergleiche eine Methode zum Parsen und einen Datei-Reader

    NumberFormatException erweitert IllegalArgumentException, das wiederum RuntimeException erweitert. Daher ist bei einem Aufruf von Integer.parseInt keine Ausnahmebehandlung vorgeschrieben.

    IOException erweitert Exception außerhalb dieses Zweigs. Ein Aufruf von Files.readString muss die mögliche IOException abfangen oder mit throws deklarieren. Eine Dateioperation kann aus äußeren Gründen fehlschlagen, selbst wenn der aufrufende Code korrekt ist.

  9. 9
    HerausforderungOrdne einen unbekannten Fehler ein

    BadRecordException erweitert Exception direkt. Eine Hilfsmethode deklariert, dass sie diesen Typ werfen kann. Was muss ihr Aufrufer tun und wann wird diese Pflicht geprüft?

    ---reveal---

    Er muss ihn abfangen oder einen passenden geprüften Typ in der Deklaration der aufrufenden Methode angeben. Der Compiler setzt diese Pflicht durch, weil die Klasse außerhalb der ungeprüften Zweige liegt. Dass ein bestimmter Programmlauf erfolgreich ist, hebt die Anforderung an die Deklaration nicht auf.

  10. 10
    HerausforderungErkläre eine überraschend allgemeine Ausnahmebehandlung

    Ein Block zum Lesen einer Datei fängt Exception ab und gibt „Dateiproblem“ aus. Stattdessen schlägt er mit einer durch einen Programmfehler verursachten NullPointerException fehl. Fängt die Ausnahmebehandlung sie ab und ist die Meldung zuverlässig?

    ---reveal---

    Ja: NullPointerException erweitert RuntimeException, das Exception erweitert. Die Meldung ist irreführend, weil dieser Fehler ein Programmfehler und kein Beleg für ein Dateiproblem ist. Lies den Stacktrace und beschränke die Ausnahmebehandlung für erwartete Fehler auf die Fehler, die sie erklären kann.

  11. 11
    ZusammenfassungWähle den nächsten Schritt bei der Fehlersuche

    Beginne die Untersuchung eines Fehlers mit seiner Klasse, seiner Meldung und dem ersten Anwendungsrahmen. Verfolge die Aufrufer und den Ursprung fehlerhafter Werte, wenn die Anweisung, in der der Fehler erkannt wurde, seine Ursache allein nicht erklärt.

    Prüfe dann die Vererbung, bevor du eine Behandlung wählst: Geprüfte Ausnahmen verlangen eine für den Compiler sichtbare Entscheidung; ungeprüfte Programmfehler müssen trotzdem korrigiert werden. Verhindere, dass allgemeine Ausnahmebehandlungen eine Art von Problem als eine andere ausgeben.

2

Verfolge Pfade der Fehlerbehandlung und der Aufräumarbeiten

12 Schritte
Verfolge eine fehlgeschlagene Eingabeumwandlung, wähle Umfang und Reihenfolge der Ausnahmebehandlungen und verfolge finally bei vorzeitigem Verlassen.

Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.

  1. 1
    EinführungEntscheide, was als Nächstes geschehen soll
  2. 2
    BeispielSetze nach der gesamten Anweisung fort
  3. 3
    TippLies bei einem erneuten Versuch neuen Text
  4. 4
    HerausforderungBehebe den Stillstand des Scanners
  5. 5
    ErklärungGib einem bestimmten Fehler eine eigene Reaktion
  6. 6
    BeispielLass die Schleife weiterlaufen
  7. 7
    HerausforderungVerschiebe die Grenze des geschützten Bereichs
  8. 8
    KernaussageTeile Reaktionen ohne überlappende Typen
  9. 9
    ErklärungAufräumarbeiten laufen vor dem Verlassen
  10. 10
    HerausforderungVerfolge einen nicht passenden Fehler
  11. 11
    HerausforderungVerhindere, dass Aufräumarbeiten einen Fehler vor dem erneuten Versuch verbergen
  12. 12
    ZusammenfassungVerfolge die nächste erreichbare Anweisung
3

Lehne eine Anfrage ab, ohne den Zustand zu beschädigen

11 Schritte
Prüfe eine Lagerbestandsoperation, mache einen fachlichen Fehler erkennbar und verpacke einen Fehler beim Parsen, ohne seine Ursache zu verlieren.

Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.

  1. 1
    EinführungErhalte ein verwendbares Objekt
  2. 2
    BeispielPrüfe vor der Subtraktion
  3. 3
    ErklärungUnterscheide throw von der Deklaration
  4. 4
    HerausforderungWähle die passende Standardausnahme
  5. 5
    VerbindungEine Überschreibung hält Zusagen gegenüber Aufrufern ein
  6. 6
    BeispielGib unzureichendem Bestand einen eigenen Typ
  7. 7
    BeispielHalte die fehlgeschlagene Anfrage getrennt
  8. 8
    HerausforderungEntscheide, welcher Zustand erhalten bleibt
  9. 9
    ErklärungErhalte den ursprünglichen Umwandlungsfehler
  10. 10
    HerausforderungGib einen Dateifehler durch eine eingeschränkte Methode weiter
  11. 11
    ZusammenfassungEntwirf eine Ausnahme, die der Aufrufer nutzen kann
4

Öffne den richtigen Text und schließe ihn sicher

12 Schritte
Löse einen Pfad auf, stimme Textkonventionen ab und verfolge dann das Öffnen und Schließen von Ressourcen sowie konkurrierende Fehler.

Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.

  1. 1
    EinführungSpeicherort und Lebensdauer sind beide wichtig
  2. 2
    BeispielLöse vom Arbeitsverzeichnis aus auf
  3. 3
    ErklärungDekodiere mit dem beim Schreiben verwendeten Zeichensatz
  4. 4
    TippEntferne ein vollständiges Zeilenende
  5. 5
    HerausforderungErhalte eine Dezimalzahl beim Schreiben und Zurücklesen
  6. 6
    ErklärungDeklariere eine zu schließende Ressource
  7. 7
    VerbindungNutze den Vertrag zum Schließen
  8. 8
    BeispielKehre die Öffnungsreihenfolge um
  9. 9
    HerausforderungVerfolge ein fehlgeschlagenes zweites Öffnen
  10. 10
    KernaussageErhalte einen Fehler im Rumpf als Hauptmeldung
  11. 11
    HerausforderungUnterscheide Datenfehler von Fehlern beim Aufräumen
  12. 12
    ZusammenfassungUntersuche eine Dateioperation der Reihe nach
5

Wandle Messwerte in einen verlässlichen Bericht um

16 Schritte
Lies und prüfe einfache Datensätze, berechne Ergebnisse aus akzeptierten Daten und speichere die fertige Ausgabe mit einer klaren Fehlerbehandlung.

Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.

  1. 1
    EinführungEin Bericht braucht mehr als korrekte Rechnungen
  2. 2
    ErklärungWähle, wie die Zeilen eingelesen werden
  3. 3
    BeispielDefiniere den Datensatz vor dem Parsen
  4. 4
    BeispielPrüfe vor der Verwendung des zweiten Felds
  5. 5
    HerausforderungFinde den verworfenen Datensatz
  6. 6
    ErklärungWähle zwischen Ersetzen und Anhängen
  7. 7
    BeispielSchreibe eine übertragbare Berichtszeile
  8. 8
    TippErkenne einen fehlgeschlagenen Schreibvorgang
  9. 9
    HerausforderungErhalte alte und neue Protokolleinträge
  10. 10
    VerbindungBehandle Fehler auf zwei verschiedenen Ebenen
  11. 11
    BeispielAktualisiere Summen erst nach der Validierung
  12. 12
    HerausforderungBehandle einen Wert, der sich parsen lässt, aber nicht geeignet ist
  13. 13
    ErklärungStelle die Ausgabe fertig, bevor du das Ziel ersetzt
  14. 14
    TippHalte den Speicherfehler sichtbar
  15. 15
    HerausforderungEntscheide, was sich zuverlässig berichten lässt
  16. 16
    ZusammenfassungPrüfe das Verarbeitungsprogramm anhand der Fehlerpfade

Ausnahmen und Dateien in Java: Übungsaufgaben

40 Übungsfragen in 4 Sets

Set 1: Verfolge Fehler und wähle eine sinnvolle Fehlerbehandlung10 Übungsfragen

Frage 1 von 10

Ein Array hat die Länge 5. Was geschieht, wenn das Programm values[5] auswertet?
  1. Es liest das fünfte Element des Arrays.
  2. Es wirft ArrayIndexOutOfBoundsException.
  3. Es gibt den Ganzzahlstandardwert 0 zurück.
  4. Es wirft NumberFormatException.
Antwort zeigenAntwort ausblenden
Antwort: B
Ein Array der Länge 5 hat die gültigen Indizes 0 bis 4, daher liegt Index 5 außerhalb des Arrays. Java wirft bei diesem Zugriff eine ArrayIndexOutOfBoundsException. Dass es fünf Elemente gibt, macht Index 5 nicht gültig: Das fünfte Element steht an Index 4.

Frage 2 von 10

DataProblem erweitert Exception direkt. Welche Pflicht hat eine Methode, wenn sie eine mit throws DataProblem deklarierte Hilfsmethode aufruft?
  1. RuntimeException abfangen oder deklarieren.
  2. Nur deklarieren, wenn die Hilfsmethode tatsächlich fehlschlägt.
  3. Nur während der Programmausführung behandeln.
  4. DataProblem abfangen oder deklarieren.
Antwort zeigenAntwort ausblenden
Antwort: D
DataProblem ist geprüft, weil es Exception außerhalb des RuntimeException-Zweigs erweitert. Der aufrufende Code muss die Ausnahme abfangen oder einen passenden geprüften Typ deklarieren. Der Compiler prüft die deklarierte Möglichkeit, selbst wenn ein bestimmter Aufruf erfolgreich wäre.

Frage 3 von 10

Welche Buchstaben werden in welcher Reihenfolge ausgegeben?

System.out.print("P");
try {
    int n = Integer.parseInt("7q");
    System.out.print("Q");
} catch (NumberFormatException e) {
    System.out.print("R");
}
System.out.print("S");
  1. PRS
  2. PQRS
  3. PR
  4. PS
Antwort zeigenAntwort ausblenden
Antwort: A
Die Umwandlung von „7q“ wirft vor der Anweisung zur Ausgabe von Q eine NumberFormatException. Die passende Ausnahmebehandlung gibt R aus, dann wird die Ausführung nach der Anweisung fortgesetzt und gibt S aus. Die übersprungene Anweisung wird nach der Behandlung nicht ausgeführt, daher kann PQRS nicht die Ausgabe sein.

Frage 4 von 10

Ein Block kann NoSuchFileException oder eine andere IOException werfen. Welche catch-Reihenfolge gibt jeder eine eigene Reaktion?
  1. IOException, dann NoSuchFileException.
  2. Exception, dann NoSuchFileException.
  3. NoSuchFileException, dann IOException.
  4. IOException | NoSuchFileException in einer Klausel.
Antwort zeigenAntwort ausblenden
Antwort: C
NoSuchFileException erweitert IOException, daher muss ihre spezifische Ausnahmebehandlung zuerst stehen. Stünde IOException zuerst, würde sie bereits jede Ausnahme für eine fehlende Datei abfangen und die spätere Ausnahmebehandlung unerreichbar machen. Auch ein Multi-Catch kann diese verwandten Typen nicht verbinden.

Frage 5 von 10

Was gibt der Aufruf von pick() aus und zurück?

static int pick() {
    try {
        return 8;
    } finally {
        System.out.print("done");
    }
}
  1. Er gibt 8 zurück, ohne etwas auszugeben.
  2. Er gibt done aus und gibt 8 zurück.
  3. Er gibt done aus und gibt keinen Wert zurück.
  4. Er gibt 8 aus und gibt dann done zurück.
Antwort zeigenAntwort ausblenden
Antwort: B
Der Rückgabeausdruck wird ausgewertet, aber die Methode führt finally aus, bevor sie diesen Wert an ihren Aufrufer übergibt. Sie gibt daher done aus und gibt 8 zurück. Ein return innerhalb von try umgeht die Aufräumarbeiten nicht; nur eine konkurrierende Kontrollflussänderung aus den Aufräumarbeiten würde dieses Ergebnis ersetzen.

Frage 6 von 10

Eine Methode lehnt eine negative Kapazität ab, bevor sie diese einem Feld zuweist. Welche Standardausnahme beschreibt das ungültige Argument am besten?
  1. IllegalArgumentException
  2. IllegalStateException
  3. NumberFormatException
  4. UnsupportedOperationException
Antwort zeigenAntwort ausblenden
Antwort: A
Eine negative Kapazität verletzt die Argumentregel der Methode, daher beschreibt IllegalArgumentException den Fehler. IllegalStateException würde einen Aufruf beschreiben, den der aktuelle Zustand des Objekts verbietet. Es ist kein Parsen angegeben, daher macht ein numerisches Argument allein dies nicht zu einem Zahlenformatfehler.

Frage 7 von 10

Ein Programm startet mit dem Arbeitsverzeichnis /work/app. Es erzeugt Path.of("data", "levels.txt"). Welchen absoluten Speicherort bezeichnet dieser relative Pfad?
  1. /work/data/levels.txt
  2. /data/levels.txt
  3. /work/app/src/data/levels.txt
  4. /work/app/data/levels.txt
Antwort zeigenAntwort ausblenden
Antwort: D
Ein relativer Pfad beginnt im Arbeitsverzeichnis, daher erweitern die beiden Pfadelemente /work/app zu /work/app/data/levels.txt. Java fügt keinen Quellordner ein. Die Datei neben den Quellcode zu legen würde nur funktionieren, wenn dies zufällig der Speicherort wäre, den der aufgelöste Pfad bezeichnet.

Frage 8 von 10

Ressource a wird geöffnet, aber das Öffnen von Ressource b wirft eine Ausnahme. Beide sind in dieser Reihenfolge im selben try-with-resources-Kopf deklariert. Auch das Schließen von a wirft eine Ausnahme. Welcher Fehler erreicht den Aufrufer?
  1. Der Fehler beim Schließen; der Fehler beim Öffnen wird unterdrückt.
  2. Der Fehler beim Öffnen; der Fehler beim Schließen wird verworfen.
  3. Der Fehler beim Öffnen; der Fehler beim Schließen wird unterdrückt.
  4. Beide Fehler getrennt in der Reihenfolge ihres Auftretens.
Antwort zeigenAntwort ausblenden
Antwort: C
Wenn das Öffnen von b fehlschlägt, schließt Java das bereits geöffnete a trotzdem. Der Fehler beim Öffnen bleibt primär und der spätere Fehler beim Schließen wird als unterdrückt hinzugefügt. Den Fehler beim Schließen als primär zu wählen, verwechselt try-with-resources mit manuellen Aufräumarbeiten, die einen früheren Fehler überschreiben können.

Frage 9 von 10

Ein BufferedReader hat das Ende seiner Datei erreicht. Was gibt das nächste readLine() zurück?
  1. null
  2. Einen leeren String.
  3. Einen String mit "EOF".
  4. Einen String mit "\n".
Antwort zeigenAntwort ausblenden
Antwort: A
BufferedReader.readLine() gibt null zurück, wenn keine Zeichen für eine weitere Zeile übrig sind. Ein leerer String steht für eine tatsächliche Leerzeile, sodass seine Verwendung als Endemarkierung zu früh stoppen würde. Zeilenenden werden aus zurückgegebenen Zeilen entfernt und sind keine Markierung für das Dateiende.

Frage 10 von 10

Eine UTF-8-Protokolldatei kann bereits existieren oder fehlen. Mit welchen Optionen erhält Files.writeString vorhandenen Text und fügt neuen Text am Ende hinzu?
  1. CREATE und TRUNCATE_EXISTING
  2. CREATE und APPEND
  3. APPEND ohne CREATE
  4. CREATE_NEW und WRITE
Antwort zeigenAntwort ausblenden
Antwort: B
APPEND schreibt nach vorhandenem Inhalt und CREATE erlaubt das Erstellen einer fehlenden Datei. Nur APPEND zu verwenden schlägt fehl, wenn das Protokoll nicht existiert. TRUNCATE_EXISTING würde vorhandenen Inhalt löschen, während CREATE_NEW einen bereits vorhandenen Pfad ablehnt.
Set 2: Wende Verträge auf Datensätze und Dateiausgaben an10 Übungsfragen

Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.

Frage 1 von 10

Ein Stacktrace beginnt mit diesen Stapelrahmen. Welche Zeile in deiner Klasse solltest du zuerst untersuchen?

java.lang.NumberFormatException: For input string: "six"
    at java.base/java.lang.NumberFormatException.forInputString(...)
    at java.base/java.lang.Integer.parseInt(...)
    at Importer.readCount(Importer.java:18)
    at Importer.main(Importer.java:6)
  1. Zeile 6 in Importer.main.
  2. Den Bibliotheksrahmen in forInputString.
  3. Zeile 18 in Importer.readCount.
  4. Die Zeile nach Zeile 18 in Importer.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 2 von 10

AmountProblem erweitert IllegalArgumentException. Wie wird sie eingeordnet?
  1. Ungeprüft, über RuntimeException.
  2. Geprüft, weil sie eine eigene Klasse ist.
  3. Geprüft, weil ihre Oberklasse eine Ausnahme ist.
  4. Ungeprüft, über den Error-Zweig.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 3 von 10

Eine Wiederholungsschleife fängt InputMismatchException von Scanner.nextInt() ab, gibt eine Meldung aus und ruft sofort wieder nextInt() auf. Das nächste Token ist „many“. Warum schlägt die Schleife weiter fehl?
  1. Die Ausnahmebehandlung setzt den gesamten Eingabestrom zurück.
  2. Der Scanner wandelt das Token in null um.
  3. Die Ausnahme schließt den Scanner dauerhaft.
  4. Der fehlgeschlagene Lesevorgang lässt dasselbe Token ungelesen.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 4 von 10

Ein Interface deklariert void load() throws IOException. Eine Implementierung muss einen Fehler wegen einer fehlenden Datei deklarieren. Welche geprüfte Deklaration ist bei ihrer Überschreibung erlaubt?
  1. throws Exception
  2. throws NoSuchFileException
  3. throws Throwable
  4. throws InterruptedException
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 5 von 10

Ein Lagerbestand enthält 17 Marker. Seine remove-Methode lehnt Anfragen über dem aktuellen Bestand ab, bevor sie die Map ändert. Aufrufe entfernen 6 und fordern dann 14 an; der zweite Fehler wird mit seiner Ursache verpackt und vom Aufrufer abgefangen. Welcher Bestand und welche Diagnoseinformation bleiben?
  1. 11 Marker, aber nur die neue Meldung der umschließenden Ausnahme.
  2. -3 Marker und der ursprüngliche Fehler als Ursache.
  3. 11 Marker und der ursprüngliche Fehler als Ursache.
  4. 17 Marker und der ursprüngliche Fehler als Ursache.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 6 von 10

Was geschieht, wenn Double.parseDouble("6,25") auf einem Computer mit deutschen Gebietsschemaeinstellungen ausgeführt wird?
  1. Die Methode gibt den Dezimalwert 6,25 zurück.
  2. Die Methode gibt den Ganzzahlwert 625 zurück.
  3. Die Methode wirft MalformedInputException.
  4. Die Methode wirft NumberFormatException.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 7 von 10

Welches Interface muss ein Objekt implementieren, damit es als Ressource in einer try-with-resources-Anweisung deklariert werden kann?
  1. AutoCloseable
  2. Serializable
  3. Iterable
  4. Comparable
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 8 von 10

Die Zeile lautet „Nora,“. Welcher Unterschied zwischen line.split(",") und line.split(",", -1) ist wichtig?
  1. Beide geben zwei Felder mit jeweils leerem zweiten Feld zurück.
  2. Das erste erhält das Komma; das zweite entfernt es.
  3. Das erste gibt ein Feld zurück; das zweite erhält ein leeres zweites Feld.
  4. Das erste wirft eine Ausnahme; das zweite gibt ein Feld zurück.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 9 von 10

Ein Programm schreibt über PrintWriter, aber eine volle Festplatte verhindert das Speichern des Texts. Was sollte es verwenden, um den vom Writer gespeicherten Fehler zu erkennen?
  1. IOException direkt von println abfangen.
  2. checkError() aufrufen und das Ergebnis prüfen.
  3. getFileName() des Dateipfads prüfen.
  4. Annehmen, dass das Schließen erfolgreiches Speichern garantiert.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 10 von 10

Ein Bericht muss ein vorhandenes Ergebnis vor teilweisem Ersetzen schützen. Lesen und Berechnung sind abgeschlossen. Welche Speicherstrategie ist angemessen?
  1. Direkt ins Ziel schreiben; bei einem Fehler von vorn beginnen.
  2. Eine temporäre Datei schreiben; bei fehlgeschlagenem atomarem Verschieben nicht atomar verschieben.
  3. Prüfen, ob das Ziel existiert; den neuen Bericht an das alte Ergebnis anhängen.
  4. Eine temporäre Datei im selben Ordner schreiben und schließen; atomares Verschieben anfordern und nicht unterstütztes Ersetzen melden.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Set 3: Verfolge zusammenwirkende Fehler und Aufräumarbeiten10 Übungsfragen

Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.

Frage 1 von 10

Welche Ausnahmefamilie fängt auch NumberFormatException ab, wenn eine Ausnahmebehandlung für ihre Oberklasse verwendet wird?
  1. RuntimeException
  2. IOException
  3. Error
  4. FileNotFoundException
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 2 von 10

Welcher Wert wird ausgegeben?

int count;
try {
    count = Integer.parseInt("bad");
} catch (NumberFormatException e) {
    count = 4;
}
System.out.println(count + 2);
  1. 4
  2. 2
  3. 6
  4. Nichts; count ist nicht initialisiert.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 3 von 10

Welcher Wert erreicht die abschließende Ausgabe?

int total = 0;
try {
    for (String text : new String[] {"8", "bad", "3"}) {
        try {
            total += Integer.parseInt(text);
        } catch (NumberFormatException e) {
            throw new IllegalStateException("record", e);
        }
    }
} catch (IllegalStateException e) {
    total += 2;
}
System.out.println(total);
  1. 13
  2. 10
  3. 11
  4. 2
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 4 von 10

Was tut result()?

static int result() {
    try {
        throw new IllegalArgumentException("bad");
    } finally {
        return 9;
    }
}
  1. Die Methode wirft die Ausnahme und gibt nie 9 zurück.
  2. Die Methode gibt 9 zurück und wirft dann die Ausnahme an den Aufrufer.
  3. Die Methode kompiliert nicht, weil finally nicht zurückkehren darf.
  4. Die Methode gibt 9 zurück und verwirft die ausstehende Ausnahme.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 5 von 10

Eine Methode zum Parsen fängt NumberFormatException e ab. Ihre eigene InvalidRecordException hat Konstruktoren nur für eine Meldung sowie für eine Meldung und eine Ursache. Wie sollte sie „Line 8“ ergänzen und dabei den ursprünglichen Stacktrace erhalten?
  1. throw new InvalidRecordException(e.getMessage());
  2. throw new InvalidRecordException("Line 8", e);
  3. throw new InvalidRecordException("Line 8");
  4. throw new RuntimeException("Line 8");
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 6 von 10

Eine Windows-Textdatei enthält Ganzzahlen zeilenweise mit CRLF als Zeilenende. Der Code verwendet Files.readString(path).split("\n") und anschließend Integer.parseInt für jeden nicht leeren Teil. Warum kann die Umwandlung eines Teils mit den Ziffern „27“ fehlschlagen?
  1. parseInt verlangt immer einen Dezimalpunkt.
  2. UTF-8 kann Windows-Zeilenenden nicht darstellen.
  3. Das Aufteilen kann ein abschließendes \r im Teil zurücklassen.
  4. Das Aufteilen wandelt die Ziffern automatisch in Bytes um.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 7 von 10

Die Ressourcen a und b werden in dieser Reihenfolge erfolgreich geöffnet. Der Rumpf wirft E; das Schließen von b wirft F; das Schließen von a wirft G. Was erreicht das catch, das zu dieser try-with-resources-Anweisung gehört?
  1. E, mit F und dann G als unterdrückten Fehlern.
  2. G, mit E und dann F als unterdrückten Fehlern.
  3. F, mit G und dann E als unterdrückten Fehlern.
  4. E allein, weil das Schließen nach F stoppt.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 8 von 10

Eine kleine Datei beginnt mit „name,age“, gefolgt von „Omar,36“. Warum sollte die erste Zeile getrennt behandelt werden, bevor das Altersfeld umgewandelt wird?
  1. Sie verwendet eine andere Zeichenkodierung als die Daten.
  2. Ihr Komma verhindert, dass split zwei Felder findet.
  3. Ein Reader gibt sie automatisch als leere Zeile zurück.
  4. Sie benennt die Spalten, statt ein ganzzahliges Alter anzugeben.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 9 von 10

Ein Verarbeitungsprogramm liest „zone,temperature“ als Kopfzeile, dann „north,2.0“, „east,bad“ und „west,8.0“. Es soll fehlerhafte Datensätze überspringen und einen Bericht über die gültigen speichern. Derzeit umschließt ein catch die gesamte Schleife zum Parsen. Welche Änderung erfüllt die Vorgabe?
  1. Das catch außen lassen; die Schleife nach der Ausnahmebehandlung automatisch fortsetzen.
  2. Jede Summe ins Ziel schreiben, bevor der nächste Datensatz geparst wird.
  3. Datensatzfehler in jedem Schleifendurchlauf abfangen; erst nach Lesen und Berechnung speichern.
  4. Bei jedem Fehler beim Parsen ein leeres Ergebnis vom Reader zurückgeben.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 10 von 10

Eine Eingabe wird mit Files.exists geprüft und dann mit einem Scanner gelesen. Seine Schleife endet nach nur einigen Datensätzen, weil der zugrunde liegende Reader fehlschlug, und das Programm beginnt sofort zu speichern. Was muss sich ändern, damit diese unvollständige Eingabe nicht als Erfolg behandelt wird?
  1. Nach dem Lesen ioException() prüfen und bei gespeichertem Lesefehler das Speichern stoppen.
  2. Nach dem Lesen Files.exists wiederholen und bei true speichern.
  3. Bei jedem Lesevorgang nur NumberFormatException abfangen und dann speichern.
  4. Den Scanner schließen und annehmen, dass die Datei vollständig gelesen wurde.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Set 4: Diagnostiziere und verbessere ein vollständiges Verarbeitungsprogramm10 Übungsfragen

Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.

Frage 1 von 10

Was wird ausgegeben, bevor die Ausnahme main verlässt, und welcher Anwendungsrahmen steht in ihrem Stacktrace zuerst?

public class Counter {
    static int read() {
        return Integer.parseInt("9z");
    }
    static void show() {
        System.out.print("L");
        System.out.print(read());
        System.out.print("M");
    }
    public static void main(String[] args) {
        show();
        System.out.print("N");
    }
}
  1. LM; Counter.show(Counter.java:7).
  2. Nur L; Counter.main(Counter.java:11).
  3. LMN; Counter.read(Counter.java:3).
  4. Nur L; Counter.read(Counter.java:3).
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 2 von 10

Ein catch-Block meldet eine ungültige numerische Eingabe, setzt aber mit einem zuvor zugewiesenen Standardwert fort. Was muss für diesen Standardwert gelten, damit die Fortsetzung sinnvoll ist?
  1. Er muss immer der Ganzzahlwert null sein.
  2. Er muss ein gültiger Wert für die restliche Berechnung sein.
  3. Er muss dem Text entsprechen, dessen Umwandlung fehlschlug.
  4. Er muss innerhalb der fehlgeschlagenen Anweisung zugewiesen werden.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 3 von 10

Eine Methode zum Parsen verwendet catch (NumberFormatException | IllegalArgumentException e). Warum lässt sich dieser Multi-Catch nicht kompilieren?
  1. Keine der Alternativen ist eine geprüfte Ausnahme.
  2. Die Alternativen benötigen zwei verschiedene Variablennamen.
  3. Eine Alternative ist eine Unterklasse der anderen.
  4. Multi-Catch erlaubt nur dateibezogene Fehler.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 4 von 10

Was gibt choose() aus und zurück?

static int choose() {
    int value = 5;
    try {
        return value;
    } finally {
        value = 12;
        System.out.print(value);
    }
}
  1. Die Methode gibt 12 aus und gibt 5 zurück.
  2. Die Methode gibt 12 aus und gibt 12 zurück.
  3. Die Methode gibt 5 aus und gibt 5 zurück.
  4. Die Methode gibt nichts aus und gibt 12 zurück.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 5 von 10

Das Interface ReaderTask deklariert String read() throws IOException. Die Klasse FixedTask implementiert es mit public String read() und ohne Deklaration einer geprüften Ausnahme. Die Variable task hat den Typ ReaderTask und verweist auf ein FixedTask-Objekt. Was muss Code tun, der task.read() aufruft?
  1. Die Behandlung weglassen, weil die Implementierung keine geprüfte Ausnahme deklariert.
  2. RuntimeException statt des deklarierten geprüften Typs abfangen.
  3. throws IOException vor dem Aufruf zur Implementierung hinzufügen.
  4. IOException gemäß der Interface-Deklaration abfangen oder deklarieren.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 6 von 10

Eine überschreibende Methode darf keine geprüften Ausnahmen hinzufügen, ruft aber Dateicode auf, der IOException werfen kann. Welche Standardhülle erhält diese Ursache und gibt eine ungeprüfte Ausnahme weiter?
  1. new IOException(e.getMessage())
  2. new UncheckedIOException(e)
  3. new Exception(e)
  4. new RuntimeException("error")
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 7 von 10

Ein Ressourcenrumpf gibt 6 zurück. Sein close() wirft dann IOException, und das zugehörige catch gibt 2 zurück; sein finally-Block gibt „closed“ aus, ohne eine Ausnahme zu werfen. Was tut die Methode?
  1. Sie gibt closed aus und gibt 2 zurück.
  2. Sie gibt closed aus und gibt 6 zurück.
  3. Sie wirft IOException und gibt keinen Wert zurück.
  4. Sie gibt 6 zurück, ohne in das catch zu gelangen.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 8 von 10

Eine Datei hat die Kopfzeile „label,count“, dann „oak,7“, „ash,“, „elm,11“ und „fir,x“. Das Verarbeitungsprogramm überspringt die Kopfzeile, erhält leere Felder beim Aufteilen, prüft beide Felder und fängt Datensatzfehler innerhalb der Schleife ab. Welche Anzahl gültiger Datensätze, welche Summe und welche tatsächlichen Zeilennummern verworfener Datensätze ergeben sich?
  1. 3 Datensätze, Summe 18; nur Zeile 5 verwerfen.
  2. 1 Datensatz, Summe 7; Zeilen 3, 4 und 5 verwerfen.
  3. 2 Datensätze, Summe 18; Zeilen 3 und 5 verwerfen.
  4. 2 Datensätze, Summe 18; Zeilen 2 und 4 verwerfen.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 9 von 10

Eine UTF-8-Datendatei muss eine Zeile „sample,7.50“ enthalten, deren zweites Feld auf Computern mit beliebigem Gebietsschema mit Double.parseDouble lesbar ist. Welcher Schreibansatz erfüllt dieses Format?
  1. "sample," + String.format("%.2f", 7.5) und ein Zeilenende schreiben.
  2. "sample," + String.format(Locale.ROOT, "%.2f", 7.5) und ein Zeilenende schreiben.
  3. "sample," + String.format(Locale.GERMANY, "%.2f", 7.5) und ein Zeilenende schreiben.
  4. "sample," + Integer.toString((int) 7.5) und ein Zeilenende schreiben.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 10 von 10

Ein Verarbeitungsprogramm liest eine gültige Kopfzeile und drei ungültige Datensätze. Es muss akzeptierte Daten und übersprungene Datensätze melden und einen vorhandenen Bericht vor teilweisem Ersetzen schützen. Welches Ergebnis und welche Speicherreihenfolge passen?
  1. Keine gültigen Messwerte und drei übersprungene Zeilen melden; temporäre Ausgabe vor dem Schließen ihres Writers ins Ziel verschieben.
  2. Keine gültigen Messwerte und vier übersprungene Zeilen melden; temporäre Ausgabe schließen und atomares Ersetzen anfordern.
  3. Keine gültigen Messwerte und drei übersprungene Zeilen melden; das Ziel direkt ersetzen, dann den Puffer leeren und den Writer schließen.
  4. Keine gültigen Messwerte und drei übersprungene Zeilen melden; temporäre Ausgabe schließen und atomares Ersetzen anfordern.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Hängst du bei deiner eigenen Aufgabe fest? Hausaufgabenhilfe

Schlüsselbegriffe

64 Schlüsselbegriffe, jeweils mit Definition

A

Absoluter Pfad
Ein absoluter Pfad gibt einen Speicherort unabhängig vom Arbeitsverzeichnis ausgehend vom Wurzelverzeichnis des Dateisystems an, gegebenenfalls einschließlich eines Laufwerksstamms.
Der Aufruf von toAbsolutePath() zeigt den absoluten Pfad, der bei der Suche nach einer im falschen Ordner abgelegten Datei hilft.
Anhängen
Anhängen fügt neue Ausgabe am Ende einer vorhandenen Datei hinzu und erhält dabei die bereits dort gespeicherten Zeichen.
Das Anhängen eines Protokolleintrags erhält frühere Einträge, fügt aber vor dem neuen Text kein fehlendes Trennzeichen ein.
Arbeitsverzeichnis
Das Arbeitsverzeichnis ist der aktuelle Ausgangsordner des Programms für relative Pfade und kann sich vom Quellcodeordner unterscheiden.
Mit /work/app als Arbeitsverzeichnis zeigt data/counts.txt auf einen Ort innerhalb von /work/app/data.
ArithmeticException
Eine ArithmeticException ist eine ungeprüfte Ausnahme, die einen außergewöhnlichen arithmetischen Zustand wie Ganzzahldivision durch null anzeigt.
Die Berechnung eines ganzzahligen Mittelwerts mit einem leeren Array kann bei der Division eine ArithmeticException werfen.
Atomares Verschieben
Atomares Verschieben ändert den Speicherort einer Datei, sofern unterstützt, in einer einzigen Dateisystemoperation; das Ersetzen eines vorhandenen Ziels hängt von der Implementierung ab.
Atomares Verschieben macht einen fertigen Bericht auf einmal sichtbar; nicht unterstütztes Ersetzen wird als Speicherfehler gemeldet.
Aufräumen
Aufräumen bezeichnet Arbeit, die beim Ende einer Operation Ressourcen freigibt oder eine Einstellung wiederherstellt, auch auf Pfaden, die wegen eines Fehlers verlassen werden.
Das Aufräumen schließt eine geöffnete Datei auch dann, wenn die Umwandlung eines ihrer Datensätze eine Ausnahme wirft.
Aufrufstapel
Der Aufrufstapel ist die Kette der in einem Thread gerade aktiven Methodenaufrufe, wobei jeder Aufruf auf die von ihm angeforderte Arbeit wartet.
Wenn main die Methode load aufruft, die wiederum parse aufruft, hält der Aufrufstapel alle drei aktiven Methodenaufrufe fest.
Ausnahme
Eine Ausnahme ist ein Objekt, das ein Ereignis beschreibt, das ein laufendes Programm unterbricht und den Kontrollfluss zu einer passenden Ausnahmebehandlung weiterleitet.
Eine Ausnahme beim Parsen von „red“ stoppt die Zuweisung, bevor das Programm ihr numerisches Ergebnis verwenden kann.
Ausnahmehierarchie
Eine Ausnahmehierarchie ist eine Vererbungsstruktur, mit der Aufrufer einen bestimmten Fehler oder eine gemeinsame Oberklasse verwandter Fehler abfangen können.
Eine Ausnahmehierarchie kann Fehler für unbekannte Artikel und unzureichenden Bestand unter einem gemeinsamen Lagerbestands-Basistyp einordnen.
Ausnahmemaskierung
Ausnahmemaskierung tritt auf, wenn eine spätere Kontrollflussänderung einen früheren Fehler ersetzt und die ursprüngliche Ausnahme vor dem Aufrufer verbirgt.
Ein return in finally kann eine Ausnahmemaskierung verursachen, indem es einen im try-Block aufgetretenen Fehler verwirft.
Ausnahmeverkettung
Ausnahmeverkettung erhält eine ursprüngliche Ausnahme als Ursache einer neuen Ausnahme, die einen nützlicheren Typ oder Diagnosekontext ergänzt.
Ausnahmeverkettung ergänzt die Zeilennummer des fehlerhaften Datensatzes, ohne den ursprünglichen Stacktrace der Umwandlung zu verlieren.
Ausnahmeweitergabe
Ausnahmeweitergabe ist die Weitergabe einer unbehandelten Ausnahme durch umschließenden Code und aufrufende Methoden nach außen, bis eine Ausnahmebehandlung gefunden wird.
Die Ausnahmeweitergabe trägt einen fehlgeschlagenen Lesevorgang aus einer Hilfsmethode zum Lesen von Dateien zu einem Aufrufer, der nach einer anderen Datei fragen kann.
Ausstehende Rückkehr
Eine ausstehende Rückkehr ist eine Rückkehr, deren Ausdruck bereits ausgewertet wurde, deren Übergabe an den Aufrufer aber auf den Abschluss erforderlicher Aufräumarbeiten wartet.
Das Ändern einer lokalen Ganzzahl während finally wertet den Ausdruck einer ausstehenden Rückkehr nicht erneut aus.
AutoCloseable
AutoCloseable ist das Interface, das close deklariert und damit ein Objekt für das automatische Schließen durch eine try-with-resources-Anweisung geeignet macht.
Eine Hilfsklasse, die AutoCloseable implementiert, kann ihren Namen ausgeben, wenn die automatischen Aufräumarbeiten close() aufrufen.

B

Benutzerdefinierte Ausnahme
Eine benutzerdefinierte Ausnahme ist eine von der Anwendung definierte werfbare Klasse, mit der Aufrufer einen aussagekräftigen fachlichen Fehler erkennen und behandeln können.
Ein Lagerbestand verwendet eine benutzerdefinierte Ausnahme, um unzureichenden Bestand von einem ungültigen Argument zu unterscheiden.
BufferedReader
Ein BufferedReader liest Zeicheneingaben mithilfe eines Puffers effizient und kann jeweils eine Zeile ohne ihr Zeilenende zurückgeben.
Eine BufferedReader-Schleife verarbeitet eine große Eingabe, bis readLine() am Ende null zurückgibt.
BufferedWriter
Ein BufferedWriter schreibt Zeichenausgabe über einen Puffer, unterstützt Zeilenenden und meldet Ein- und Ausgabefehler mit IOException.
Ein BufferedWriter schreibt eine Berichtszeile und verwendet dann newLine(), um das Zeilenende der Plattform hinzuzufügen.

C

catch-Klausel
Eine catch-Klausel benennt einen werfbaren Typ und eine Ausnahmebehandlung, die ausgeführt wird, wenn eine Ausnahme aus der geschützten Arbeit zu diesem Typ passt.
Eine catch-Klausel für NumberFormatException kann nach einer anderen Zahl fragen, nachdem der Benutzer „many“ eingegeben hat.
Catch-or-declare-Regel
Die Catch-or-declare-Regel verlangt, dass Code, der eine geprüfte Ausnahme werfen kann, sie behandelt oder einen passenden Typ in seiner Methodendeklaration aufführt.
Die Catch-or-declare-Regel gilt für eine Methode, die Files.readString aufruft, selbst wenn die gelesene Datei existiert.
Closeable
Closeable ist ein AutoCloseable-Unterinterface für Ein- und Ausgaberessourcen, dessen close-Methode IOException statt der allgemeineren Exception deklariert.
Ein BufferedReader implementiert Closeable und eignet sich daher für eine try-with-resources-Deklaration.
CSV
CSV ist Text mit kommagetrennten Werten, der üblicherweise einen Datensatz pro Zeile speichert; Felder in Anführungszeichen können Kommas enthalten und erfordern korrektes CSV-Parsen.
Einfaches CSV mit „Mina,28“ kann aufgeteilt werden, während ein Komma in einem Namen in Anführungszeichen einen CSV-Parser benötigt.

D

Dateifehler
Ein Dateifehler verhindert den zuverlässigen Abschluss einer Dateioperation, etwa eine fehlende Eingabe oder ein Festplattenfehler beim Speichern eines Berichts.
Ein Dateifehler beim Lesen muss das Speichern stoppen, damit unvollständige Eingaben nicht als vollständiges Ergebnis ausgegeben werden.
Datenfeld
Ein Datenfeld ist ein einzelner Wert innerhalb eines strukturierten Datensatzes, etwa der Name oder das Alter, die in einer einfachen Textdatenzeile durch ein Komma getrennt sind.
Das zweite Datenfeld in „Mina,“ ist leer, wenn mit einer negativen Begrenzung aufgeteilt wird, die leere Felder am Ende erhält.
Datensatzfehler
Ein Datensatzfehler macht einen Eingabedatensatz unbrauchbar, erlaubt aber bei einer Regel zum Überspringen und Melden die Verarbeitung späterer Datensätze.
Ein Datensatzfehler meldet „Zeile 6: ungültige Temperatur“ und lässt den nächsten Schleifendurchlauf erreichbar.

E

Error
Ein Error ist ein Throwable-Untertyp für schwerwiegende Laufzeit- oder Umgebungsfehler, den eine gewöhnliche Anwendung bei der Fehlerbehandlung nicht abzufangen versuchen sollte.
OutOfMemoryError gehört zu Error; das Abfangen gewöhnlicher ungültiger Eingaben behebt keinen ausgeschöpften Heap.

F

Fail-fast-Prinzip
Das Fail-fast-Prinzip bedeutet, ungültige Eingaben oder Zustände an der Stelle ihrer Erkennung abzulehnen, bevor spätere Arbeit das Problem verbreiten oder verschleiern kann.
Nach dem Fail-fast-Prinzip prüft ein Setter einen ungültigen Prozentwert, bevor er dem Feld etwas zuweist.
finally-Block
Ein finally-Block enthält Aufräumcode, der beim Verlassen seiner try-Anweisung ausgeführt wird, auch nach normaler Arbeit, einer Ausnahmebehandlung, einer Rückkehr oder einer geworfenen Ausnahme.
Der finally-Block gibt eine Aufräummeldung aus, bevor ein durch return vorbereiteter Wert den Aufrufer erreicht.

G

Gebietsschema
Ein Gebietsschema legt sprachliche und regionale Formatierungskonventionen fest, die das Parsen von Zahlen durch Scanner und die formatierte Dezimalausgabe beeinflussen können.
Die Wahl des Root-Gebietsschemas sorgt dafür, dass formatierte Dezimaldaten unabhängig von den Computereinstellungen „3.50“ verwenden.
Geprüfte Ausnahme
Eine geprüfte Ausnahme ist ein Throwable-Untertyp außerhalb der Zweige RuntimeException und Error, für den die Catch-or-declare-Regel gilt.
Eine IOException ist eine geprüfte Ausnahme, daher muss eine Hilfsmethode zum Lesen von Dateien sie abfangen oder deklarieren.
Geschützter Bereich
Ein geschützter Bereich ist die von einer try-Anweisung umschlossene Arbeit; seine Grenze bestimmt, was abgebrochen wird, wenn ein Fehler ihre Ausnahmebehandlung erreicht.
Der geschützte Bereich umfasst einen Datensatz, wenn eine try-Anweisung zur Umwandlung innerhalb des Schleifenrumpfs steht.

I

IllegalArgumentException
Eine IllegalArgumentException ist eine ungeprüfte Ausnahme, die verwendet wird, wenn ein Aufrufer einen Wert übergibt, der die Anforderungen einer Methode an ihre Argumente verletzt.
Eine Methode kann IllegalArgumentException werfen, wenn für ihre erforderliche positive Menge -2 übergeben wird.
IllegalStateException
Eine IllegalStateException ist eine ungeprüfte Ausnahme, die verwendet wird, wenn der aktuelle Zustand eines Objekts die angeforderte Operation nicht erlaubt.
Eine bereits versandte Bestellung kann IllegalStateException werfen, wenn Code versucht, ihr einen weiteren Artikel hinzuzufügen.
InputMismatchException
Eine InputMismatchException ist eine ungeprüfte Scanner-Ausnahme, die auftritt, wenn ein Token nicht zum Typ oder Wertebereich passt, den eine Umwandlungsmethode verlangt.
nextInt() wirft bei „several“ eine InputMismatchException und lässt dieses Token zum Verwerfen verfügbar.

K

Kopfzeile
Eine Kopfzeile benennt die Spalten in einer Datendatei und wird getrennt behandelt, damit ihre Bezeichnungen nicht als gewöhnliche Datensatzwerte geparst werden.
Die Kopfzeile „name,age“ muss übersprungen oder geprüft werden, bevor die Methode zum Parsen versucht, ein Alter umzuwandeln.

L

Leeren des Puffers
Das Leeren des Puffers sendet die im Puffer eines Writers wartende Ausgabe an ihr Ziel weiter; es gibt die offene Ressource nicht selbst frei.
Das Leeren des Puffers sorgt dafür, dass ausstehender Text den Puffer des Writers verlässt, aber das Programm muss seine Dateiressource weiterhin schließen.

M

Multi-Catch
Ein Multi-Catch ist eine einzelne catch-Klausel, die nicht durch Vererbung verbundene Ausnahmetypen mit einem senkrechten Strich verbindet, sodass sie denselben Rumpf zur Ausnahmebehandlung teilen.
Ein Multi-Catch kann sowohl bei einem fehlerhaften Ganzzahlfeld als auch bei einem fehlenden Array-Element einen fehlerhaft formatierten Datensatz melden.

N

NullPointerException
Eine NullPointerException ist eine ungeprüfte Ausnahme, die auftritt, wenn eine Operation ein Objekt benötigt, aber eine Nullreferenz erhält oder verwendet.
Der Aufruf von name.trim() wirft eine NullPointerException, wenn name null ist, bevor Text ohne umgebenden Leerraum zurückgegeben wird.
NumberFormatException
Eine NumberFormatException ist eine ungeprüfte Ausnahme, die auftritt, wenn eine numerische Textumwandlung den angeforderten Zahlenwert nicht erzeugen kann.
Integer.parseInt("blue") wirft NumberFormatException, weil der Text keine Ganzzahl darstellen kann.

P

Parsen
Parsen interpretiert Text gemäß einer erwarteten Struktur und wandelt Felder in Werte oder Objekte um, die das Programm verwenden kann.
Beim Parsen eines Datensatzes mit Name und Alter werden seine zwei Felder geprüft, bevor das Alter umgewandelt und eine Person erzeugt wird.
Path
Ein Path ist ein Objekt, das einen Speicherort in einem Dateisystem darstellt; sein Erzeugen öffnet, erstellt oder prüft die angegebene Datei nicht.
Path.of("data", "counts.txt") erzeugt einen Path, ohne zu prüfen, ob der Datenordner existiert.
PrintWriter
Ein PrintWriter schreibt formatierte Zeichenausgabe mit print-Methoden, die Ein- und Ausgabefehler intern speichern, damit checkError sie melden kann.
Ein PrintWriter kann einen Mittelwert mit printf formatieren, aber die erfolgreiche Rückkehr dieses Aufrufs beweist nicht, dass das Speichern gelang.
Puffer
Ein Puffer ist ein Speicherbereich, der Text vorübergehend hält, bevor er an eine Datei weitergesendet wird, und dadurch viele kleine Ausgabevorgänge reduziert.
Der Puffer eines Writers kann die letzte Berichtszeile noch halten, bis er geleert oder der Writer geschlossen wird.

R

Reihenfolge der catch-Klauseln
Die Reihenfolge der catch-Klauseln ist die Abfolge von oben nach unten, in der Ausnahmebehandlungen geprüft werden. Dabei muss die Behandlung für eine Unterklasse vor der für ihre Oberklasse stehen.
Die Reihenfolge der catch-Klauseln setzt NoSuchFileException vor IOException, wenn beide eine eigene Meldung benötigen.
Relativer Pfad
Ein relativer Pfad bezeichnet einen Speicherort ausgehend vom Arbeitsverzeichnis, statt am Wurzelverzeichnis des Dateisystems oder eines Laufwerks zu beginnen.
Der relative Pfad data/counts.txt kann eine andere Datei bezeichnen, wenn das Programm in einem anderen Ordner startet.
Ressource
Eine Ressource ist ein Objekt, das ein externes Betriebsmittel wie eine offene Datei hält, das nach Ende seiner Verwendung freigegeben werden muss.
Ein Datei-Reader ist eine Ressource, die auch dann geschlossen werden sollte, wenn die Verarbeitung ihrer nächsten Zeile fehlschlägt.
RuntimeException
RuntimeException ist die Exception-Unterklasse, deren Nachfolger für ungeprüfte Fehler stehen, darunter ungültige Argumente und die Verwendung einer Nullreferenz.
NumberFormatException erreicht RuntimeException über ihre Oberklasse IllegalArgumentException.

S

Scanner
Ein Scanner liest Text als Token oder Zeilen und bietet Umwandlungsmethoden an, während er zugrunde liegende Ein- und Ausgabefehler beim Lesen zur späteren Prüfung speichert.
Ein Datei-Scanner benötigt nach seiner Schleife eine Prüfung mit ioException(), damit ein fehlgeschlagener Lesevorgang nicht erfolgreich erscheint.
Stacktrace
Ein Stacktrace gibt eine Ausnahme und ihre aktiven Methodenaufrufe an, beginnend bei der Erkennungsstelle und weiter über deren Aufrufer.
Schlägt bei einer von main aufgerufenen Hilfsmethode eine Umwandlung fehl, zeigt der Stacktrace zuerst die Umwandlung, dann die Hilfsmethode und main.

T

Temporäre Datei
Eine temporäre Datei hält eine Zwischenausgabe getrennt vom endgültigen Ziel, sodass sie fertiggestellt werden kann, bevor das Ersetzen versucht wird.
Eine temporäre Datei für einen Bericht wird im Zielordner erstellt, bevor der fertige Text an seinen Platz verschoben wird.
throw
Das Schlüsselwort throw beginnt eine Anweisung, die ein Ausnahmeobjekt aus der aktuellen Operation wirft und deren normale Ausführung unterbricht.
Ein throw vor der Feldzuweisung lehnt eine ungültige Kapazität ab und erhält dabei den alten Feldwert.
Throwable
Throwable ist die Java-Oberklasse von Objekten, die geworfen werden können; ihre beiden direkten Hauptunterklassen sind Error und Exception.
Eine eigene geprüfte Ausnahme gehört über Exception zu Throwable, sodass Java sie werfen und abfangen kann.
throws-Klausel
Eine throws-Klausel ist Teil einer Methodendeklaration, der mögliche weitergegebene Ausnahmetypen aufführt und die Pflicht zur Behandlung geprüfter Ausnahmen an Aufrufer weitergibt.
Die throws-Klausel throws IOException erlaubt einer Datei-Hilfsmethode, einen Fehler an einen Aufrufer zu melden, der darauf reagieren kann.
Trunkierung
Trunkierung entfernt den Inhalt einer vorhandenen Datei, wenn sie zum Ersetzen geöffnet wird, bevor neue Ausgabe notwendigerweise erfolgreich geschrieben wurde.
Das standardmäßige Files.writeString verursacht eine Trunkierung, sodass ein späterer Schreibfehler ein unvollständiges Ergebnis hinterlassen kann.
try-Block
Ein try-Block umschließt Arbeit, deren Ausnahmen durch zugehörige catch-Klauseln behandelt werden können; bei einem Fehler werden seine übrigen Anweisungen übersprungen.
Ein try-Block, der Text in eine Ganzzahl umwandelt und sie dann ausgibt, stoppt vor der Ausgabeanweisung, wenn die Umwandlung fehlschlägt.
try-with-resources-Anweisung
Eine try-with-resources-Anweisung ist eine Java-Anweisung, die erfolgreich geöffnete AutoCloseable-Ressourcen beim Verlassen der geschützten Arbeit automatisch schließt.
Eine try-with-resources-Anweisung schließt einen Writer vor dem Reader, wenn ihre Deklarationen im Kopf den Reader zuerst öffnen.

U

UncheckedIOException
Eine UncheckedIOException ist eine Standardunterklasse von RuntimeException, die eine IOException verpackt und den ursprünglichen Dateifehler als Ursache erhält.
Eine überschreibende Methode, die IOException nicht deklarieren darf, kann einen Dateifehler mit UncheckedIOException weitergeben.
Unerreichbare Ausnahmebehandlung
Eine unerreichbare Ausnahmebehandlung ist eine catch-Klausel, die keine Ausnahme erhalten kann, weil eine frühere Ausnahmebehandlung bereits alle passenden Typen abdeckt.
Steht IOException zuerst und wird danach NoSuchFileException abgefangen, entsteht eine unerreichbare Ausnahmebehandlung.
Ungeprüfte Ausnahme
Eine ungeprüfte Ausnahme gehört zu RuntimeException oder Error oder einer ihrer Unterklassen; für sie besteht keine Pflicht zum Abfangen oder Deklarieren.
Ein falscher Array-Index verursacht eine ungeprüfte Ausnahme, selbst wenn das Programm erfolgreich kompiliert wurde.
Unterdrückte Ausnahme
Eine unterdrückte Ausnahme ist ein zusätzlicher Fehler, der einer primären Ausnahme hinzugefügt wird, etwa ein Fehler beim Schließen, nachdem ein Ressourcenrumpf bereits eine Ausnahme geworfen hat.
Die unterdrückte Ausnahme erhält den Fehler beim Schließen eines Readers, während der ursprüngliche Fehler beim Parsen weitergegeben wird.
Ursache
Eine Ursache ist das ursprüngliche werfbare Objekt, das in einer umschließenden Ausnahme erhalten bleibt und über getCause für die Diagnose verfügbar ist.
Die Ursache einer InvalidRecordException kann die NumberFormatException sein, die ihr numerisches Feld ausgelöst hat.
UTF-8
UTF-8 ist eine Unicode-Zeichenkodierung, die ein Byte für einfache lateinische Zeichen und bis zu vier Bytes für andere Unicode-Zeichen verwendet.
In UTF-8 belegt „café“ fünf Bytes, obwohl es nur vier sichtbare Zeichen enthält.

W

Wiederherstellung
Wiederherstellung ist die Reaktion des Programms, die ihm nach einem Fehler eine sinnvolle Fortsetzung ermöglicht, etwa ein erneuter Versuch oder das Überspringen eines fehlerhaften Datensatzes.
Bei der Wiederherstellung in einer Eingabeschleife wird vor einer erneuten Umwandlung eine neue Zeile gelesen, sodass das Programm fortschreiten kann.

Z

Zeichenkodierung
Eine Zeichenkodierung ordnet Textzeichen gespeicherten Bytes zu und legt fest, wie ein Reader diese Bytes wieder als Text interpretiert.
Eine nicht passende Zeichenkodierung kann UTF-8-Text wie „café“ als verstümmelten Text wie „café“ anzeigen.
Zeilenende
Ein Zeilenende ist die Zeichenfolge, die eine Textzeile abschließt, üblicherweise LF unter Linux und macOS oder CRLF unter Windows.
Ein von BufferedReader.readLine() gelesenes Zeilenende wird entfernt, bevor die Methode den Zeilentext zurückgibt.

Über diese Einheit

Von StudeliFehler melden

Erstelle deine eigenen Lernmaterialien

Mach aus deinen Notizen, Folien und Dokumenten Lernleitfäden, Übungsfragen, Karteikarten und 6 weitere Tools.

Jetzt starten