Von Jamal Hassan, Backend Developer bei Java Fleet Systems Consulting
Mit Einblicken von Nova Trent (Junior Dev) und Elyndra Valen (Senior Dev)
Schwierigkeit: 🟡 Mittel
Lesezeit: 40 Minuten
Voraussetzungen: Java Grundlagen, Try-with-Resources
Kurs: Java Erweiterte Techniken – Tag 7 von 10
📖 Java Erweiterte Techniken – Alle Tage
📍 Du bist hier: Tag 7
⚡ Das Wichtigste in 30 Sekunden
Dein Problem: Du musst Konfigurationen laden, Logs schreiben, CSV-Dateien verarbeiten oder Verzeichnisse durchsuchen.
Die Lösung: Die java.nio.file API – modern, sicher und mit Stream-Support.
Heute lernst du:
- ✅
PathundPaths– Dateipfade plattformunabhängig - ✅
Files– Die Schweizer Taschenmesser-Klasse - ✅ Textdateien lesen und schreiben
- ✅ Properties-Dateien laden und speichern
- ✅ Verzeichnisse erstellen und durchlaufen
- ✅ Try-with-Resources für sichere Ressourcenverwaltung
Für wen ist dieser Artikel?
- 🌱 Anfänger: Du lernst Dateien sicher zu lesen und schreiben
- 🌿 Erfahrene: Properties, Verzeichnis-Streams, Best Practices
- 🌳 Profis: Performance, große Dateien, Watch Service
👋 Jamal: „Dateien sind überall“
Hi! 👋
Jamal hier für Tag 7. Heute übernehme ich, weil File I/O mein täglich Brot ist – Config-Dateien, Logs, Datenexporte…
Das alte Java (vor 1.7):
// Umständlich und fehleranfällig
File file = new File("config.txt");
FileReader fr = null;
BufferedReader br = null;
try {
fr = new FileReader(file);
br = new BufferedReader(fr);
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try { if (br != null) br.close(); } catch (IOException e) {}
try { if (fr != null) fr.close(); } catch (IOException e) {}
}
Modernes Java (NIO.2):
// Elegant und sicher
Path path = Path.of("config.txt");
Files.readAllLines(path).forEach(System.out::println);
Von 15 Zeilen auf 2. Das ist der Unterschied!
🖼️ Die NIO.2 Architektur

Abbildung 1: Path, Files und die wichtigsten Operationen
🟢 GRUNDLAGEN
Path – Der moderne Dateipfad
Path ersetzt das alte File-Objekt und ist plattformunabhängig:
// Path erstellen
Path path1 = Path.of("dokumente", "bericht.txt");
Path path2 = Path.of("/home/user/dokumente/bericht.txt");
Path path3 = Paths.get("config.properties"); // Alternativ
// Pfad-Informationen
System.out.println(path1.getFileName()); // bericht.txt
System.out.println(path1.getParent()); // dokumente
System.out.println(path1.toAbsolutePath()); // /home/.../dokumente/bericht.txt
System.out.println(path1.getNameCount()); // 2
// Pfade kombinieren
Path base = Path.of("/home/user");
Path full = base.resolve("dokumente/bericht.txt");
// /home/user/dokumente/bericht.txt
// Relativen Pfad berechnen
Path from = Path.of("/home/user");
Path to = Path.of("/home/user/dokumente/bericht.txt");
Path relative = from.relativize(to); // dokumente/bericht.txt
Pfad normalisieren:
Path messy = Path.of("/home/user/../user/./dokumente//bericht.txt");
Path clean = messy.normalize();
// /home/user/dokumente/bericht.txt
Files – Die Utility-Klasse
Files bietet statische Methoden für alle Dateioperationen:
Path path = Path.of("test.txt");
// Prüfungen
boolean exists = Files.exists(path);
boolean isFile = Files.isRegularFile(path);
boolean isDir = Files.isDirectory(path);
boolean readable = Files.isReadable(path);
boolean writable = Files.isWritable(path);
// Datei-Infos
long size = Files.size(path);
FileTime modified = Files.getLastModifiedTime(path);
String mimeType = Files.probeContentType(path); // "text/plain"
Textdateien lesen
Ganze Datei auf einmal:
// Als Liste von Zeilen
List<String> zeilen = Files.readAllLines(Path.of("daten.txt"));
// Als ein String
String inhalt = Files.readString(Path.of("daten.txt"));
// Mit Encoding
List<String> zeilen = Files.readAllLines(
Path.of("daten.txt"),
StandardCharsets.UTF_8
);
Zeile für Zeile (für große Dateien):
// Mit Stream – lazy, speichereffizient!
try (Stream<String> lines = Files.lines(Path.of("große-datei.txt"))) {
lines
.filter(line -> !line.startsWith("#")) // Kommentare ignorieren
.map(String::trim)
.forEach(System.out::println);
}
// Mit BufferedReader
try (BufferedReader reader = Files.newBufferedReader(Path.of("daten.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
Textdateien schreiben
Einfach:
// String schreiben
Files.writeString(Path.of("ausgabe.txt"), "Hallo Welt!");
// Liste von Zeilen
List<String> zeilen = List.of("Zeile 1", "Zeile 2", "Zeile 3");
Files.write(Path.of("ausgabe.txt"), zeilen);
// Anhängen statt überschreiben
Files.writeString(
Path.of("log.txt"),
"Neue Log-Zeile\n",
StandardOpenOption.APPEND,
StandardOpenOption.CREATE
);
Mit BufferedWriter:
try (BufferedWriter writer = Files.newBufferedWriter(Path.of("ausgabe.txt"))) {
writer.write("Zeile 1");
writer.newLine();
writer.write("Zeile 2");
writer.newLine();
}
Try-with-Resources – Niemals vergessen!
KRITISCH: Streams und Reader/Writer müssen immer geschlossen werden!
// ✅ RICHTIG: Try-with-Resources
try (Stream<String> lines = Files.lines(path)) {
lines.forEach(System.out::println);
} // Stream wird automatisch geschlossen
// ❌ FALSCH: Resource Leak!
Stream<String> lines = Files.lines(path);
lines.forEach(System.out::println);
// Stream bleibt offen → Datei blockiert!
Mehrere Ressourcen:
try (
BufferedReader reader = Files.newBufferedReader(input);
BufferedWriter writer = Files.newBufferedWriter(output)
) {
String line;
while ((line = reader.readLine()) != null) {
writer.write(line.toUpperCase());
writer.newLine();
}
}
🟡 PROFESSIONALS
Properties-Dateien – Konfiguration richtig gemacht

Abbildung 2: Properties laden, verwenden, speichern
Properties sind der Standard für Konfigurationsdateien in Java:
config.properties:
# Datenbank-Konfiguration db.host=localhost db.port=5432 db.name=myapp db.user=admin db.password=geheim123 # Anwendungs-Einstellungen app.name=MeineApp app.version=1.0.0 app.debug=true app.maxConnections=10
Properties laden:
// Aus Datei laden
Properties props = new Properties();
try (InputStream input = Files.newInputStream(Path.of("config.properties"))) {
props.load(input);
}
// Werte lesen
String host = props.getProperty("db.host");
String port = props.getProperty("db.port", "3306"); // Mit Default
int maxConn = Integer.parseInt(props.getProperty("app.maxConnections", "5"));
boolean debug = Boolean.parseBoolean(props.getProperty("app.debug", "false"));
System.out.println("Verbinde zu " + host + ":" + port);
Properties aus Classpath laden (für JAR-Dateien):
Properties props = new Properties();
try (InputStream input = getClass().getResourceAsStream("/config.properties")) {
if (input == null) {
throw new FileNotFoundException("config.properties nicht im Classpath!");
}
props.load(input);
}
Properties speichern:
Properties props = new Properties();
props.setProperty("app.name", "MeineApp");
props.setProperty("app.version", "2.0.0");
props.setProperty("last.run", LocalDateTime.now().toString());
try (OutputStream output = Files.newOutputStream(Path.of("config.properties"))) {
props.store(output, "Anwendungskonfiguration - Nicht manuell bearbeiten!");
}
Ergebnis:
#Anwendungskonfiguration - Nicht manuell bearbeiten! #Mon Jan 20 14:30:00 CET 2025 app.name=MeineApp app.version=2.0.0 last.run=2025-01-20T14\:30\:00
Properties iterieren:
// Alle Eigenschaften ausgeben
props.forEach((key, value) ->
System.out.println(key + " = " + value));
// Als Stream
props.stringPropertyNames().stream()
.filter(key -> key.startsWith("db."))
.forEach(key -> System.out.println(key + " = " + props.getProperty(key)));
Verzeichnisse erstellen und durchlaufen
Verzeichnisse erstellen:
// Ein Verzeichnis
Files.createDirectory(Path.of("neuer-ordner"));
// Verschachtelte Verzeichnisse (wie mkdir -p)
Files.createDirectories(Path.of("pfad/zu/verschachteltem/ordner"));
Verzeichnis-Inhalt auflisten:
// Einfache Liste
try (Stream<Path> stream = Files.list(Path.of("."))) {
stream.forEach(System.out::println);
}
// Rekursiv durchlaufen
try (Stream<Path> stream = Files.walk(Path.of("projekt"))) {
stream
.filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(".java"))
.forEach(System.out::println);
}
// Mit maximaler Tiefe
try (Stream<Path> stream = Files.walk(Path.of("projekt"), 2)) {
stream.forEach(System.out::println);
}
Dateien finden:
// Alle .txt Dateien finden
try (Stream<Path> stream = Files.find(
Path.of("dokumente"),
10, // Max Tiefe
(path, attr) -> path.toString().endsWith(".txt") && attr.isRegularFile()
)) {
stream.forEach(System.out::println);
}
Dateien kopieren, verschieben, löschen
Path quelle = Path.of("original.txt");
Path ziel = Path.of("kopie.txt");
// Kopieren
Files.copy(quelle, ziel);
Files.copy(quelle, ziel, StandardCopyOption.REPLACE_EXISTING);
// Verschieben
Files.move(quelle, ziel);
Files.move(quelle, ziel, StandardCopyOption.ATOMIC_MOVE);
// Löschen
Files.delete(path); // Wirft Exception wenn nicht existiert
Files.deleteIfExists(path); // Gibt false zurück wenn nicht existiert
// Verzeichnis rekursiv löschen
try (Stream<Path> walk = Files.walk(Path.of("zu-loeschen"))) {
walk.sorted(Comparator.reverseOrder()) // Dateien vor Ordnern!
.forEach(p -> {
try { Files.delete(p); }
catch (IOException e) { e.printStackTrace(); }
});
}
Binärdateien
// Binär lesen
byte[] bytes = Files.readAllBytes(Path.of("bild.png"));
// Binär schreiben
Files.write(Path.of("kopie.png"), bytes);
// Mit Streams für große Dateien
try (
InputStream in = Files.newInputStream(Path.of("große-datei.bin"));
OutputStream out = Files.newOutputStream(Path.of("kopie.bin"))
) {
in.transferTo(out); // Java 9+
}
🔵 BONUS
Temporäre Dateien und Verzeichnisse
// Temporäre Datei
Path tempFile = Files.createTempFile("prefix-", ".tmp");
System.out.println(tempFile); // /tmp/prefix-12345.tmp
// Temporäres Verzeichnis
Path tempDir = Files.createTempDirectory("myapp-");
// Automatisch löschen bei JVM-Ende
tempFile.toFile().deleteOnExit();
CSV-Dateien verarbeiten
// CSV lesen
Path csv = Path.of("daten.csv");
List<String[]> daten = Files.lines(csv)
.skip(1) // Header überspringen
.map(line -> line.split(";"))
.toList();
// CSV schreiben
List<String[]> export = List.of(
new String[]{"Name", "Alter", "Stadt"},
new String[]{"Max", "25", "Berlin"},
new String[]{"Anna", "30", "München"}
);
List<String> lines = export.stream()
.map(arr -> String.join(";", arr))
.toList();
Files.write(Path.of("export.csv"), lines);
Watch Service – Dateien überwachen
WatchService watchService = FileSystems.getDefault().newWatchService();
Path dir = Path.of("überwacht");
dir.register(watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.ENTRY_DELETE
);
System.out.println("Überwache " + dir + "...");
while (true) {
WatchKey key = watchService.take(); // Blockiert
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
Path fileName = (Path) event.context();
System.out.println(kind + ": " + fileName);
}
key.reset();
}
💬 Real Talk: File I/O Fallen vermeiden
Java Fleet Büro, Donnerstag 11:00. Nova kämpft mit Dateien.
Nova: „Jamal, mein Programm funktioniert auf meinem Mac, aber auf dem Windows-Server crasht es!“
Path config = Path.of("/home/user/config.properties"); // Unix-Pfad!
Jamal: „Klassiker! Hardcodierte Pfade sind Gift. Nutze relative Pfade oder System-Properties:“
// Besser: Relativ zum Arbeitsverzeichnis
Path config = Path.of("config", "app.properties");
// Oder: Home-Verzeichnis des Users
Path userConfig = Path.of(System.getProperty("user.home"), ".myapp", "config.properties");
// Oder: Aus Classpath laden
getClass().getResourceAsStream("/config.properties");
Nova: „Und warum ist meine Datei nach dem Lesen leer?“
List<String> lines = Files.readAllLines(path); Files.write(path, processedLines); // Überschreibt!
Jamal: „Du überschreibst die Originaldatei. Schreib in eine neue Datei, dann umbenennen:“
Path temp = Path.of("daten.tmp");
Files.write(temp, processedLines);
Files.move(temp, path, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
Elyndra: kommt vorbei „Und vergesst nicht: Files.lines() muss in try-with-resources!“
// ❌ Resource Leak!
Files.lines(path).forEach(System.out::println);
// ✅ Korrekt
try (Stream<String> lines = Files.lines(path)) {
lines.forEach(System.out::println);
}
❓ FAQ
Frage 1: File oder Path – was soll ich nutzen?
Path (NIO.2) ist der moderne Standard seit Java 7:
- Plattformunabhängig
- Bessere API
- Stream-Support
- Mehr Funktionen
File nur für Legacy-Code oder APIs, die File erwarten:
Path path = Path.of("datei.txt");
File file = path.toFile(); // Konvertierung wenn nötig
Frage 2: Wie lade ich Properties aus einem JAR?
// FALSCH: Funktioniert nicht im JAR
Properties props = new Properties();
props.load(new FileInputStream("config.properties"));
// RICHTIG: Classpath nutzen
try (InputStream input = getClass().getResourceAsStream("/config.properties")) {
props.load(input);
}
Die Datei muss in src/main/resources/ liegen!
Frage 3: Encoding-Probleme – warum Umlaute kaputt?
Immer UTF-8 explizit angeben:
// Lesen
List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
// Schreiben
Files.writeString(path, text, StandardCharsets.UTF_8);
// Properties
try (Reader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
props.load(reader);
}
Frage 4: Wie vermeide ich FileNotFoundException?
Path path = Path.of("config.properties");
if (Files.exists(path)) {
// Datei verarbeiten
} else {
// Default-Konfiguration erstellen
Files.writeString(path, "# Default Config\napp.name=MyApp");
}
Frage 5: Bernd sagt, Properties seien „veraltet“?
seufz Properties sind nach wie vor der Standard für einfache Konfigurationen:
- ✅ In der JDK enthalten (keine Abhängigkeit)
- ✅ Einfaches Format
- ✅ Gut für Key-Value-Paare
- ✅ Internationalisierung (ResourceBundle)
Für komplexe Konfigurationen → YAML, JSON, TOML mit entsprechenden Libraries.
🔍 „behind the code“ oder „in my feels“? Die echten Geschichten findest du, wenn du weißt wo du suchen musst…
🎁 Cheat Sheet
🟢 Path erstellen
Path.of("datei.txt")
Path.of("ordner", "datei.txt")
Path.of("/absoluter/pfad")
Paths.get("datei.txt") // Alternativ
🟡 Dateien lesen
// Alles auf einmal
String text = Files.readString(path);
List<String> lines = Files.readAllLines(path);
byte[] bytes = Files.readAllBytes(path);
// Streaming (große Dateien)
try (Stream<String> lines = Files.lines(path)) { }
try (BufferedReader br = Files.newBufferedReader(path)) { }
🔵 Dateien schreiben
Files.writeString(path, "text"); Files.write(path, lines); Files.write(path, bytes); // Optionen StandardOpenOption.APPEND // Anhängen StandardOpenOption.CREATE // Erstellen wenn nicht existiert StandardOpenOption.TRUNCATE_EXISTING // Überschreiben
🟣 Properties
// Laden
Properties props = new Properties();
try (InputStream in = Files.newInputStream(path)) {
props.load(in);
}
// Lesen
String value = props.getProperty("key");
String value = props.getProperty("key", "default");
// Speichern
props.setProperty("key", "value");
try (OutputStream out = Files.newOutputStream(path)) {
props.store(out, "Kommentar");
}
🔴 Verzeichnisse
Files.createDirectory(path);
Files.createDirectories(path); // Rekursiv
try (Stream<Path> s = Files.list(path)) { } // Inhalt
try (Stream<Path> s = Files.walk(path)) { } // Rekursiv
try (Stream<Path> s = Files.walk(path, 2)) { } // Max Tiefe
🎨 Challenge für dich!
🟢 Level 1 – Einsteiger
- [ ] Lies eine Textdatei und zähle die Zeilen
- [ ] Schreibe eine Liste von Strings in eine Datei
- [ ] Prüfe ob eine Datei existiert und lesbar ist
Geschätzte Zeit: 20-30 Minuten
🟡 Level 2 – Fortgeschritten
- [ ] Lade Properties und gib alle
db.*Einträge aus - [ ] Finde alle
.javaDateien in einem Verzeichnis (rekursiv) - [ ] Kopiere alle
.txtDateien von A nach B
Geschätzte Zeit: 45-60 Minuten
🔵 Level 3 – Profi
- [ ] Implementiere einen Config-Manager mit Default-Werten
- [ ] Parse eine CSV-Datei in eine Liste von Objekten
- [ ] Erstelle einen Datei-Backup mit Timestamp
Geschätzte Zeit: 60-90 Minuten
📦 Downloads
| Projekt | Für wen? | Download |
|---|---|---|
| tag07-fileio-starter.zip | 🟢 Mit TODOs | ⬇️ Download |
| tag07-fileio-complete.zip | 🟡 Musterlösung | ⬇️ Download |
🔗 Weiterführende Links
🇩🇪 Deutsch
| Ressource | Beschreibung |
|---|---|
| Rheinwerk: Ein-/Ausgabe | Umfassendes Kapitel |
🇬🇧 Englisch
| Ressource | Beschreibung | Level |
|---|---|---|
| Oracle: File I/O Tutorial | Offizielle Doku | 🟢 |
| Baeldung: Java NIO2 | Praxisbeispiele | 🟡 |
| Baeldung: Properties | Deep Dive | 🟡 |
👋 Geschafft! 🎉
Was du heute gelernt hast:
✅ Path für plattformunabhängige Dateipfade
✅ Files als zentrale Utility-Klasse
✅ Textdateien lesen und schreiben
✅ Properties-Dateien laden und speichern
✅ Verzeichnisse erstellen und durchlaufen
✅ Try-with-Resources für sichere Ressourcenverwaltung
Fragen? Jamal.Hassan@java-developer.online
📖 Weiter geht’s!
← Vorheriger Tag: Tag 6: Stream-API
→ Nächster Tag: Tag 8: Annotations & Multithreading Basics
Tags: #Java #FileIO #NIO2 #Properties #Path #Files #Tutorial
© 2025 Java Fleet Systems Consulting | java-developer.online

