Alle Tabs der Lerneinheit (Erklärung · Interaktiv verstehen · Praxis-Übung · Klausur-Quiz) als durchgehender Text. Ideal zum Wiederholen vor der Klausur, und für Suchmaschinen wie Google, Bing und KI-Suche (ChatGPT, Perplexity).
Erklärung
Sobald dein Programm mit der Außenwelt redet, eine Datei liest, eine schreibt, übers Netzwerk kommuniziert, läuft das über Streams: einen Fluss von Daten, der Stück für Stück gelesen oder geschrieben wird. Java trennt dabei sauber zwischen Byte-Strömen (für Binärdaten) und Zeichen-Strömen (für Text). Wer das verwechselt, bekommt kaputte Umlaute oder unnötig komplizierten Code.
Was du können musst:
- Byte-Streams (
InputStream/OutputStream) von Char-Streams (Reader/Writer) unterscheiden - das Lese-Muster mit dem EOF-Signal kennen (
read()liefert -1,readLine()liefertnull) - Buffering verstehen (warum
BufferedReaderschneller ist) - Dateien sicher schließen (try-with-resources) und das richtige Encoding (UTF-8) wählen
Abgrenzung: hier die Grundlagen der Datei-I/O. Tiefes NIO (Channels, Buffers, Memory-Mapped Files, asynchrones I/O) ist Vertiefung.
Die Idee in einem Satz
Ein Stream ist ein Datenfluss:
InputStream/Readerliest Daten herein,OutputStream/Writerschreibt hinaus, Stück für Stück, bis das Ende (EOF) erreicht ist.
Byte-Streams vs. Char-Streams
| Byte-Streams | Char-Streams | |
|---|---|---|
| Basisklassen | InputStream, OutputStream | Reader, Writer |
| Einheit | Byte (0-255) | Zeichen (mit Encoding) |
| Wofür | Binärdaten: Bilder, Audio, serialisierte Objekte | Text: .txt, .csv, .json |
| EOF-Signal | read() liefert -1 | readLine() liefert null |
Faustregel: Text → Char-Stream (Reader/Writer), alles andere → Byte-Stream. Ein Char-Stream wandelt Bytes über ein Encoding (Charset, meist UTF-8) in Zeichen um; ein Byte-Stream tut das nicht.
// Byte-Stream: Datei roh als Bytes lesen
try (InputStream in = new FileInputStream("bild.png")) {
int b;
while ((b = in.read()) != -1) { /* b ist ein Byte 0-255 */ }
}
// Char-Stream: Textdatei zeilenweise lesen (mit Encoding)
try (BufferedReader br = Files.newBufferedReader(Path.of("text.txt"))) {
String zeile;
while ((zeile = br.readLine()) != null) { System.out.println(zeile); }
}
Das Lese-Muster und das EOF-Signal
Das häufigste Klausur-Detail: woran erkennt die Schleife das Dateiende? Bei einem Byte-Stream liefert read() einen int zurück, das gelesene Byte (0 bis 255) oder -1, wenn nichts mehr kommt. -1, nicht 0, denn 0 ist ein gültiges Byte. Verfolge den Lese-Loop:
Interaktive Visualisierung
Interaktive Komponente: probiere sie im Topic-Player oben aus.
Buffering: warum BufferedReader
Jeder ungepufferte read() löst potenziell einen Festplatten-Zugriff (System-Call) aus, byteweise ist das extrem langsam. Ein Buffer liest einen großen Block auf einmal in den Speicher; die folgenden read()-Aufrufe werden dann aus dem RAM bedient.
Interaktive Visualisierung
Interaktive Komponente: probiere sie im Topic-Player oben aus.
Moderne I/O: NIO (Files / Path)
Für die meisten Fälle reicht heute die knappe NIO-API aus java.nio.file:
// ganze Datei als String (kleine Dateien)
String inhalt = Files.readString(Path.of("text.txt")); // UTF-8
// alle Zeilen als Liste
List<String> zeilen = Files.readAllLines(Path.of("text.txt"));
// schreiben
Files.writeString(Path.of("out.txt"), "Hallo");
// gepufferter Reader (grosse Dateien, zeilenweise)
try (BufferedReader br = Files.newBufferedReader(Path.of("gross.txt"))) { ... }
Files.readString/readAllLinesladen die ganze Datei in den Speicher, ideal für kleine Dateien. Für große Dateien zeilenweise mitBufferedReaderstreamen.
Klausur-Faustregeln
1. Text → Char-Stream, Binär → Byte-Stream. Reader/Writer für .txt/.csv/.json, InputStream/OutputStream für Bilder, Audio, serialisierte Objekte.
2. EOF-Signale auseinanderhalten. Byte-read() liefert -1 am Ende, readLine() liefert null. Beides ist nicht 0 und nicht "" (leerer String).
3. Immer puffern. BufferedReader/BufferedInputStream macht aus vielen kleinen Festplatten-Zugriffen wenige große, oft drastisch schneller.
4. Encoding explizit setzen. Char-Streams wandeln Bytes über ein Charset (UTF-8). Ohne Angabe greift der Plattform-Default, das ist nicht portabel und zerschießt Umlaute.
5. Ressourcen mit try-with-resources schließen. Datei/Stream schließen, auch im Fehlerfall, sonst Resource-Leak.
6. NIO (Files/Path) für knappe Standard-Fälle. Files.readString, Files.readAllLines, Files.writeString ersetzen viel altes Boilerplate.
Häufige Stolpersteine
1. EOF-Bedingung falsch. while (in.read() != 0) statt != -1 läuft ewig oder bricht beim ersten Null-Byte ab. Bei readLine() ist die Bedingung != null, sonst NullPointerException.
2. Byte-Stream für Text. Direkt Bytes lesen und in chars casten ignoriert das Encoding, Mehrbyte-Zeichen (ä, ö, ü, Emojis) zerbrechen. Für Text immer Reader/Writer.
3. Nicht puffern. Byteweise von der Platte lesen ohne Buffer ist um Größenordnungen langsamer. Im Zweifel BufferedReader drumherum.
4. Datei nicht geschlossen. Ohne close() (oder try-with-resources) bleibt der File-Handle offen, Resource-Leak, bei vielen offenen Dateien "too many open files".
5. Encoding nicht angegeben. new FileReader(...) nutzt den Plattform-Default. Besser Files.newBufferedReader(path) (UTF-8) oder das Charset explizit angeben.
6. read() liefert int, nicht byte. Wer den Rückgabewert vorschnell in byte castet, kann -1 (EOF) nicht mehr von 255 unterscheiden. Erst auf -1 prüfen, dann casten.
Interaktiv verstehen
Eine Textdatei zeilenweise lesen
Der Standard-Weg, eine Textdatei zu verarbeiten: gepufferter Reader, Zeile für Zeile, bis readLine() null liefert, und automatisch geschlossen per try-with-resources. Achte im Stepper auf das null-Signal (das Char-Pendant zum -1 der Byte-Streams).
Interaktive Visualisierung
Interaktive Komponente: probiere sie im Topic-Player oben aus.
Klausur-Tipp: Zwei Fragen kommen fast immer: (1) "Wie erkennt die Schleife das Dateiende?" Antwort: read() → -1, readLine() → null. (2) "Warum BufferedReader?" Antwort: weniger Festplatten-Zugriffe (Buffer statt byteweise). Und: nenne try-with-resources fürs sichere Schließen.
Praxis-Übung
Klausurfragen mit Lösungen (6)
- F1.Welche Stream-Art nutzt man, um eine Textdatei (.txt) zeilenweise zu lesen?
Antwort: Reader / BufferedReader (Char-Stream)
Erklärung: Fuer Text nutzt man Char-Streams (Reader/BufferedReader): sie wandeln Bytes ueber ein Encoding (UTF-8) in Zeichen. Byte-Streams (InputStream) sind fuer Binaerdaten und kennen kein Encoding, Umlaute wuerden zerbrechen.
- F2.Was liefert die Byte-Methode read() zurück, wenn das Dateiende erreicht ist?
Antwort: -1
Erklärung: read() liefert das naechste Byte (0-255) oder -1 bei EOF. NICHT 0 (das ist ein gueltiges Byte) und nicht null (read gibt einen int zurueck). Das Char-Pendant readLine() liefert dagegen null bei EOF.
- F3.BufferedReader ist hauptsächlich schneller, weil er weniger Festplatten-Zugriffe macht.
Antwort: Wahr
Erklärung: Richtig. Statt pro Byte/Zeichen einen Festplatten-Zugriff (Syscall) zu machen, liest der Puffer einen grossen Block auf einmal und bedient die folgenden read()-Aufrufe aus dem Speicher. Oft Faktor 10-100 schneller.
Typ: Wahr/Falsch
- F4.Was ist der Hauptgrund, Datei-Operationen in try-with-resources zu kapseln?
Antwort: Die Datei wird automatisch geschlossen, auch wenn eine Exception fliegt
Erklärung: try-with-resources ruft close() automatisch auf, am Ende des Blocks und auch im Fehlerfall. Das verhindert Resource-Leaks (offene File-Handles). Mit Performance, Encoding oder Buffering hat es nichts zu tun.
- F5.Ordne jede Klasse ihrer Aufgabe zu.
Zuordnungen:
- InputStream → Bytes lesen (binär)
- Reader → Zeichen lesen (Text, mit Encoding)
- BufferedReader → gepuffertes zeilenweises Lesen
- OutputStream → Bytes schreiben (binär)
Erklärung: InputStream/OutputStream = Bytes (binär), Reader/Writer = Zeichen (Text). BufferedReader puffert und bietet readLine().
Typ: Zuordnung
- F6.Warum ist es ein Fehler, den Rückgabewert von read() sofort in ein byte zu casten?
Antwort: Ein Byte 255 wird als (byte) zu -1, dann bricht die Schleife fälschlich beim Wert 255 ab
Erklärung: read() gibt absichtlich einen int (0-255 oder -1) zurueck, damit EOF (-1) von jedem gueltigen Byte unterscheidbar ist. Castet man zu byte, wird 255 zu -1 (byte ist signed, -128..127), und die Schleife bricht beim Datenbyte 255 faelschlich ab. Erst auf -1 pruefen, dann casten.
byte b;
while ((b = (byte) in.read()) != -1) { ... }Klausur-Quiz
Klausurfragen mit Lösungen (6)
- F1.Womit signalisiert readLine() (Char-Stream) das Dateiende?
Antwort: null
Erklärung: readLine() liefert die naechste Zeile als String oder null bei EOF. Ein leerer String ("") ist eine gueltige Leerzeile, NICHT das Ende. Byte-read() nutzt dagegen -1.
- F2.Welche Methode lädt eine ganze (kleine) Textdatei in einem Aufruf als String?
Antwort: Files.readString(path)
Erklärung: Files.readString(path) liest die komplette Datei als String (UTF-8), ideal fuer kleine Dateien. Fuer grosse Dateien besser zeilenweise mit BufferedReader streamen, statt alles in den Speicher zu laden.
- F3.Ein Byte-Stream (InputStream) eignet sich gut, um eine Textdatei mit Umlauten zu lesen.
Antwort: Falsch
Erklärung: Falsch. Ein Byte-Stream kennt kein Encoding. Mehrbyte-Zeichen wie ä/ö/ü (in UTF-8 mehrere Bytes) zerbrechen, wenn man sie byteweise als Zeichen interpretiert. Fuer Text nimmt man Char-Streams (Reader), die ueber ein Charset dekodieren.
Typ: Wahr/Falsch
- F4.Für Binärdaten nutzt man {{1}}-Streams, für Text {{2}}-Streams. Das Lese-Ende signalisiert read() mit {{3}} und readLine() mit {{4}}.
Lösungen pro Lücke:
- {{1}}: Byte / byte
- {{2}}: Char / Zeichen / char
- {{3}}: -1
- {{4}}: null
Erklärung: Byte-Streams (binär) vs. Char-Streams (Text). EOF: read() liefert -1, readLine() liefert null. Standard-Klausurwissen der Datei-I/O.
Typ: Lückentext
- F5.Warum sollte man das Encoding (z.B. UTF-8) beim Lesen einer Textdatei explizit angeben?
Antwort: Sonst wird der Plattform-Default benutzt, was nicht portabel ist und Umlaute zerstören kann
Erklärung: Ohne explizites Charset nimmt Java den Plattform-Default (z.B. Windows-1252 vs. UTF-8). Dieselbe Datei wird dann auf verschiedenen Systemen unterschiedlich dekodiert, Umlaute und Sonderzeichen zerbrechen. Files.newBufferedReader nutzt standardmaessig UTF-8.
- F6.Was ist an dieser Schleife problematisch?
Antwort: Die Bedingung != 0 ist falsch: EOF ist -1, nicht 0; ausserdem wird die Datei nie geschlossen
Erklärung: Zwei Fehler: (1) Das EOF-Signal von read() ist -1, nicht 0, die Bedingung muss != -1 lauten (mit != 0 laeuft die Schleife ueber das Dateiende hinaus weiter). (2) fr wird nie geschlossen, Resource-Leak. Besser: try-with-resources + != -1.
FileReader fr = new FileReader("daten.txt");
int c;
while ((c = fr.read()) != 0) {
System.out.print((char) c);
}