Ausnahmen und Dateien in Java
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
- 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 StacktraceErklä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 einOrdne 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.
- Lies eine Ausnahme und ihren Stacktrace
- 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 fortSchreibe 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 ReihenfolgeVerwende 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 ausErgä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.
- Fange eine Ausnahme ab und setze das Programm sinnvoll fort
- 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 throwsPrü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 UrsacheSchreibe eigene geprüfte oder ungeprüfte Ausnahmeklassen, wirf sie vor einer Änderung des Objektzustands und verpacke eine tieferliegende Ausnahme, ohne sie zu verlieren.
- Wirf Ausnahmen und deklariere sie mit throws
- 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 ZeilenendenErstelle 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.
- Arbeite mit Pfaden, Zeichenkodierungen und Zeilenenden
- 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 umLies 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 anSchreibe 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 DateiverarbeitungVerbinde Lesen, Parsen, Ausnahmen und Schreiben in einem Programm, das bei Dateifehlern geordnet stoppt, fehlerhafte Datensätze überspringt und meldet und unvollständige Ausgaben vermeidet.
- Lies eine Textdatei ein und wandle ihre Zeilen in Daten um
Ausnahmen und Dateien in Java einfach erklärt
1Verstehe, 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.
| Ausnahme | Typischer Auslöser |
|---|---|
ArithmeticException | Ganzzahldivision durch null |
ArrayIndexOutOfBoundsException | a[3], wenn das Array die Länge 3 hat |
IndexOutOfBoundsException | list.get(list.size()) |
NullPointerException | Aufruf einer Methode über null |
NumberFormatException | Integer.parseInt("12a") |
InputMismatchException | Scanner.nextInt() trifft auf „ten“ |
ClassCastException | Typumwandlung 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)
- Lies in der ersten Zeile die Ausnahmeklasse und die Meldung.
- Lies im obersten
at-Stapelrahmen die Stelle, an der das Problem erkannt wurde. - Lies weiter nach unten zu den Aufrufern:
mainhataveragein Zeile 9 aufgerufen. - Gehören die obersten Stapelrahmen zur Java-Bibliothek, suche den ersten Stapelrahmen, der deine eigene Klasse nennt. Bei einem fehlgeschlagenen
Integer.parseIntkö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.
| Kategorie | Klassen | Anforderung des Compilers |
|---|---|---|
| Ungeprüft | RuntimeException, Error und ihre Unterklassen | Abfangen oder Deklarieren ist optional |
| Geprüft | Alle anderen Throwable-Unterklassen | Abfangen 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,Errorund 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
Exceptionkann Programmfehler verbergen. - Ordne eine unbekannte Ausnahme anhand ihrer Oberklassenkette ein.
2Behandle 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
finallywird bei normalem Abschluss, abgefangenen und nicht abgefangenen Ausnahmen sowie Kontrollflussänderungen wiereturnausgefü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;finallywird dennoch erreicht. - Eine Rückkehr oder geworfene Ausnahme in
finallykann 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.
3Wirf 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.
| Problem | Standardausnahme |
|---|---|
| Argument hat einen ungültigen Wert | IllegalArgumentException |
| Objekt befindet sich für den Aufruf im falschen Zustand | IllegalStateException |
Benötigte Objektreferenz ist null | NullPointerException |
| Operation wird von der Klasse nicht angeboten | UnsupportedOperationException |
| Position liegt außerhalb einer Collection | IndexOutOfBoundsException |
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
throwmit einem Ausnahmeobjekt, um eine ungültige Operation abzubrechen. - Prüfe die Eingaben, bevor du Felder oder Collections änderst.
throwsdeklariert 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
Exceptionfür eine geprüfte Klasse oderRuntimeExceptionfü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.
UncheckedIOExceptiongibt eineIOExceptionals ungeprüfte Ausnahme weiter.
4Bestimme 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
| Konvention | Java-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.parseDoubleverwendet 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.
5Lies 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
| Methode | Ergebnis 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
CREATEmitAPPEND, um alten Inhalt zu erhalten und eine neue Protokolldatei zu ermöglichen. - Das Schließen leert den Ausgabepuffer und gibt die Ressource frei.
PrintWriterspeichert Schreibfehler;checkError()macht sie zugänglich.BufferedWriterundFiles-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
1Verfolge einen Fehler bis zu seiner Ursache zurück
11 Schritte
- 1Einfü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.
- 2BeispielBeobachte, wie die Umwandlung abbricht
Eine Methode
loadCountführt diese Anweisungen aus:System.out.print("start "); int count = Integer.parseInt("eight"); System.out.print("finish");Sie gibt
startaus und wirft dannNumberFormatException. Die Zuweisung wird nicht abgeschlossen undfinishwird übersprungen. Eine passende Ausnahmebehandlung kann übernehmen, aber die fehlgeschlagene Anweisung wird nach der Behandlung nicht fortgesetzt. - 3ErklärungVerfolge die aktiven Aufrufe
Angenommen,
mainhatloadCountaufgerufen, das wiederumInteger.parseIntaufgerufen hat. Diese Aufrufe bilden einen Teil des Aufrufstapels. Die Ausnahme wandert nach außen, wenn eine Methode keine passende Ausnahmebehandlung hat.parseIntschlägt zuerst fehl,loadCountkann seine normale Arbeit nicht abschließen und der Aufrufer erhält die Gelegenheit, den Fehler zu behandeln. Verlässt die Ausnahmemainunbehandelt, meldet Java sie und der Hauptthread endet. - 4BeispielFinde 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
loadCountaufgerufen 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. - 5HerausforderungFinde die übersprungene Ausgabe
Eine Hilfsmethode gibt
Aaus, ruftInteger.parseInt("q2")auf und würde dannBausgeben. Ihr Aufrufer fängtNumberFormatExceptionab und gibtCaus. Welche Buchstaben werden ausgegeben und warum?---reveal---
Aund dannC. Die Umwandlung wirft vorBeine 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, umBauszugeben. - 6TippSchau ü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 undprintStackTrace()für die Aufrufkette. Eine Meldung kannnullsein; ein AbschnittCaused by:liefert eine weitere Ausnahme, die in einer Hülle erhalten blieb. - 7ErklärungNutze die Oberklassenkette
Throwableist die Basisklasse für werfbare Objekte. Ihre Hauptzweige sindErrorundException;RuntimeExceptionsteht unterException.RuntimeException,Errorund jede Unterklasse einer der beiden sind ungeprüft. AndereThrowable-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. - 8BeispielVergleiche eine Methode zum Parsen und einen Datei-Reader
NumberFormatExceptionerweitertIllegalArgumentException, das wiederumRuntimeExceptionerweitert. Daher ist bei einem Aufruf vonInteger.parseIntkeine Ausnahmebehandlung vorgeschrieben.IOExceptionerweitertExceptionaußerhalb dieses Zweigs. Ein Aufruf vonFiles.readStringmuss die möglicheIOExceptionabfangen oder mitthrowsdeklarieren. Eine Dateioperation kann aus äußeren Gründen fehlschlagen, selbst wenn der aufrufende Code korrekt ist. - 9HerausforderungOrdne einen unbekannten Fehler ein
BadRecordExceptionerweitertExceptiondirekt. 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.
- 10HerausforderungErkläre eine überraschend allgemeine Ausnahmebehandlung
Ein Block zum Lesen einer Datei fängt
Exceptionab und gibt „Dateiproblem“ aus. Stattdessen schlägt er mit einer durch einen Programmfehler verursachtenNullPointerExceptionfehl. Fängt die Ausnahmebehandlung sie ab und ist die Meldung zuverlässig?---reveal---
Ja:
NullPointerExceptionerweitertRuntimeException, dasExceptionerweitert. 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. - 11ZusammenfassungWä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.
2Verfolge Pfade der Fehlerbehandlung und der Aufräumarbeiten
12 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungEntscheide, was als Nächstes geschehen soll
- 2BeispielSetze nach der gesamten Anweisung fort
- 3TippLies bei einem erneuten Versuch neuen Text
- 4HerausforderungBehebe den Stillstand des Scanners
- 5ErklärungGib einem bestimmten Fehler eine eigene Reaktion
- 6BeispielLass die Schleife weiterlaufen
- 7HerausforderungVerschiebe die Grenze des geschützten Bereichs
- 8KernaussageTeile Reaktionen ohne überlappende Typen
- 9ErklärungAufräumarbeiten laufen vor dem Verlassen
- 10HerausforderungVerfolge einen nicht passenden Fehler
- 11HerausforderungVerhindere, dass Aufräumarbeiten einen Fehler vor dem erneuten Versuch verbergen
- 12ZusammenfassungVerfolge die nächste erreichbare Anweisung
3Lehne eine Anfrage ab, ohne den Zustand zu beschädigen
11 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungErhalte ein verwendbares Objekt
- 2BeispielPrüfe vor der Subtraktion
- 3ErklärungUnterscheide throw von der Deklaration
- 4HerausforderungWähle die passende Standardausnahme
- 5VerbindungEine Überschreibung hält Zusagen gegenüber Aufrufern ein
- 6BeispielGib unzureichendem Bestand einen eigenen Typ
- 7BeispielHalte die fehlgeschlagene Anfrage getrennt
- 8HerausforderungEntscheide, welcher Zustand erhalten bleibt
- 9ErklärungErhalte den ursprünglichen Umwandlungsfehler
- 10HerausforderungGib einen Dateifehler durch eine eingeschränkte Methode weiter
- 11ZusammenfassungEntwirf eine Ausnahme, die der Aufrufer nutzen kann
4Öffne den richtigen Text und schließe ihn sicher
12 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungSpeicherort und Lebensdauer sind beide wichtig
- 2BeispielLöse vom Arbeitsverzeichnis aus auf
- 3ErklärungDekodiere mit dem beim Schreiben verwendeten Zeichensatz
- 4TippEntferne ein vollständiges Zeilenende
- 5HerausforderungErhalte eine Dezimalzahl beim Schreiben und Zurücklesen
- 6ErklärungDeklariere eine zu schließende Ressource
- 7VerbindungNutze den Vertrag zum Schließen
- 8BeispielKehre die Öffnungsreihenfolge um
- 9HerausforderungVerfolge ein fehlgeschlagenes zweites Öffnen
- 10KernaussageErhalte einen Fehler im Rumpf als Hauptmeldung
- 11HerausforderungUnterscheide Datenfehler von Fehlern beim Aufräumen
- 12ZusammenfassungUntersuche eine Dateioperation der Reihe nach
5Wandle Messwerte in einen verlässlichen Bericht um
16 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungEin Bericht braucht mehr als korrekte Rechnungen
- 2ErklärungWähle, wie die Zeilen eingelesen werden
- 3BeispielDefiniere den Datensatz vor dem Parsen
- 4BeispielPrüfe vor der Verwendung des zweiten Felds
- 5HerausforderungFinde den verworfenen Datensatz
- 6ErklärungWähle zwischen Ersetzen und Anhängen
- 7BeispielSchreibe eine übertragbare Berichtszeile
- 8TippErkenne einen fehlgeschlagenen Schreibvorgang
- 9HerausforderungErhalte alte und neue Protokolleinträge
- 10VerbindungBehandle Fehler auf zwei verschiedenen Ebenen
- 11BeispielAktualisiere Summen erst nach der Validierung
- 12HerausforderungBehandle einen Wert, der sich parsen lässt, aber nicht geeignet ist
- 13ErklärungStelle die Ausgabe fertig, bevor du das Ziel ersetzt
- 14TippHalte den Speicherfehler sichtbar
- 15HerausforderungEntscheide, was sich zuverlässig berichten lässt
- 16ZusammenfassungPrüfe das Verarbeitungsprogramm anhand der Fehlerpfade
Ausnahmen und Dateien in Java: Übungsaufgaben
Set 1: Verfolge Fehler und wähle eine sinnvolle Fehlerbehandlung10 Übungsfragen
Frage 1 von 10
values[5] auswertet?Antwort zeigenAntwort ausblenden
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?Antwort zeigenAntwort ausblenden
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");
Antwort zeigenAntwort ausblenden
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
NoSuchFileException oder eine andere IOException werfen. Welche catch-Reihenfolge gibt jeder eine eigene Reaktion?Antwort zeigenAntwort ausblenden
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");
}
}
Antwort zeigenAntwort ausblenden
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
Antwort zeigenAntwort ausblenden
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
/work/app. Es erzeugt Path.of("data", "levels.txt"). Welchen absoluten Speicherort bezeichnet dieser relative Pfad?Antwort zeigenAntwort ausblenden
/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
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?Antwort zeigenAntwort ausblenden
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
BufferedReader hat das Ende seiner Datei erreicht. Was gibt das nächste readLine() zurück?Antwort zeigenAntwort ausblenden
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
Files.writeString vorhandenen Text und fügt neuen Text am Ende hinzu?Antwort zeigenAntwort ausblenden
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)
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 2 von 10
AmountProblem erweitert IllegalArgumentException. Wie wird sie eingeordnet?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 3 von 10
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?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 4 von 10
void load() throws IOException. Eine Implementierung muss einen Fehler wegen einer fehlenden Datei deklarieren. Welche geprüfte Deklaration ist bei ihrer Überschreibung erlaubt?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 5 von 10
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?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 6 von 10
Double.parseDouble("6,25") auf einem Computer mit deutschen Gebietsschemaeinstellungen ausgeführt wird?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 7 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 8 von 10
line.split(",") und line.split(",", -1) ist wichtig?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 9 von 10
PrintWriter, aber eine volle Festplatte verhindert das Speichern des Texts. Was sollte es verwenden, um den vom Writer gespeicherten Fehler zu erkennen?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 10 von 10
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
NumberFormatException ab, wenn eine Ausnahmebehandlung für ihre Oberklasse verwendet wird?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);
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);
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;
}
}
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 5 von 10
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?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 6 von 10
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?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 7 von 10
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?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 8 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 9 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 10 von 10
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?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");
}
}
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 2 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 3 von 10
catch (NumberFormatException | IllegalArgumentException e). Warum lässt sich dieser Multi-Catch nicht kompilieren?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);
}
}
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 5 von 10
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?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 6 von 10
IOException werfen kann. Welche Standardhülle erhält diese Ursache und gibt eine ungeprüfte Ausnahme weiter?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 7 von 10
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?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 8 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 9 von 10
Double.parseDouble lesbar ist. Welcher Schreibansatz erfüllt dieses Format?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 10 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Hängst du bei deiner eigenen Aufgabe fest? Hausaufgabenhilfe
Schlüsselbegriffe
A
Absoluter Pfad
toAbsolutePath() zeigt den absoluten Pfad, der bei der Suche nach einer im falschen Ordner abgelegten Datei hilft.Anhängen
Arbeitsverzeichnis
/work/app als Arbeitsverzeichnis zeigt data/counts.txt auf einen Ort innerhalb von /work/app/data.ArithmeticException
ArithmeticException werfen.Atomares Verschieben
Aufräumen
Aufrufstapel
main die Methode load aufruft, die wiederum parse aufruft, hält der Aufrufstapel alle drei aktiven Methodenaufrufe fest.Ausnahme
Ausnahmehierarchie
Ausnahmemaskierung
finally kann eine Ausnahmemaskierung verursachen, indem es einen im try-Block aufgetretenen Fehler verwirft.Ausnahmeverkettung
Ausnahmeweitergabe
Ausstehende Rückkehr
finally wertet den Ausdruck einer ausstehenden Rückkehr nicht erneut aus.AutoCloseable
AutoCloseable implementiert, kann ihren Namen ausgeben, wenn die automatischen Aufräumarbeiten close() aufrufen.B
Benutzerdefinierte Ausnahme
BufferedReader
BufferedReader-Schleife verarbeitet eine große Eingabe, bis readLine() am Ende null zurückgibt.BufferedWriter
BufferedWriter schreibt eine Berichtszeile und verwendet dann newLine(), um das Zeilenende der Plattform hinzuzufügen.C
catch-Klausel
NumberFormatException kann nach einer anderen Zahl fragen, nachdem der Benutzer „many“ eingegeben hat.Catch-or-declare-Regel
Files.readString aufruft, selbst wenn die gelesene Datei existiert.Closeable
BufferedReader implementiert Closeable und eignet sich daher für eine try-with-resources-Deklaration.CSV
D
Dateifehler
Datenfeld
Datensatzfehler
E
Error
OutOfMemoryError gehört zu Error; das Abfangen gewöhnlicher ungültiger Eingaben behebt keinen ausgeschöpften Heap.F
Fail-fast-Prinzip
finally-Block
return vorbereiteter Wert den Aufrufer erreicht.G
Gebietsschema
Geprüfte Ausnahme
IOException ist eine geprüfte Ausnahme, daher muss eine Hilfsmethode zum Lesen von Dateien sie abfangen oder deklarieren.Geschützter Bereich
I
IllegalArgumentException
IllegalArgumentException werfen, wenn für ihre erforderliche positive Menge -2 übergeben wird.IllegalStateException
IllegalStateException werfen, wenn Code versucht, ihr einen weiteren Artikel hinzuzufügen.InputMismatchException
nextInt() wirft bei „several“ eine InputMismatchException und lässt dieses Token zum Verwerfen verfügbar.K
Kopfzeile
L
Leeren des Puffers
M
Multi-Catch
N
NullPointerException
name.trim() wirft eine NullPointerException, wenn name null ist, bevor Text ohne umgebenden Leerraum zurückgegeben wird.NumberFormatException
Integer.parseInt("blue") wirft NumberFormatException, weil der Text keine Ganzzahl darstellen kann.P
Parsen
Path
Path.of("data", "counts.txt") erzeugt einen Path, ohne zu prüfen, ob der Datenordner existiert.PrintWriter
PrintWriter kann einen Mittelwert mit printf formatieren, aber die erfolgreiche Rückkehr dieses Aufrufs beweist nicht, dass das Speichern gelang.Puffer
R
Reihenfolge der catch-Klauseln
NoSuchFileException vor IOException, wenn beide eine eigene Meldung benötigen.Relativer Pfad
data/counts.txt kann eine andere Datei bezeichnen, wenn das Programm in einem anderen Ordner startet.Ressource
RuntimeException
NumberFormatException erreicht RuntimeException über ihre Oberklasse IllegalArgumentException.S
Scanner
Scanner benötigt nach seiner Schleife eine Prüfung mit ioException(), damit ein fehlgeschlagener Lesevorgang nicht erfolgreich erscheint.Stacktrace
T
Temporäre Datei
throw
throw vor der Feldzuweisung lehnt eine ungültige Kapazität ab und erhält dabei den alten Feldwert.Throwable
Exception zu Throwable, sodass Java sie werfen und abfangen kann.throws-Klausel
throws IOException erlaubt einer Datei-Hilfsmethode, einen Fehler an einen Aufrufer zu melden, der darauf reagieren kann.Trunkierung
Files.writeString verursacht eine Trunkierung, sodass ein späterer Schreibfehler ein unvollständiges Ergebnis hinterlassen kann.try-Block
try-with-resources-Anweisung
U
UncheckedIOException
IOException nicht deklarieren darf, kann einen Dateifehler mit UncheckedIOException weitergeben.Unerreichbare Ausnahmebehandlung
IOException zuerst und wird danach NoSuchFileException abgefangen, entsteht eine unerreichbare Ausnahmebehandlung.Ungeprüfte Ausnahme
Unterdrückte Ausnahme
Ursache
InvalidRecordException kann die NumberFormatException sein, die ihr numerisches Feld ausgelöst hat.UTF-8
W
Wiederherstellung
Z
Zeichenkodierung
Zeilenende
BufferedReader.readLine() gelesenes Zeilenende wird entfernt, bevor die Methode den Zeilentext zurückgibt.- 1Java-Programme kompilieren und starten
- 2Bedingungen und Schleifen in Java
- 3Java-Methoden und Problemzerlegung
- 4Listen, Arrays und Strings in Java
- 5Java-Klassen und Objekte
- 6Objektreferenzen in Java
- 7HashMaps und Collections in Java
- 8Vererbung in Java
- 9Ausnahmen und Dateien in Java
- 10Modultests in Java
- 11Lambda-Ausdrücke und Streams in Java
Über diese Einheit
Erstelle deine eigenen Lernmaterialien
Mach aus deinen Notizen, Folien und Dokumenten Lernleitfäden, Übungsfragen, Karteikarten und 6 weitere Tools.