Lesson 12 of 13
Files, I/O & Modern Java Features
Learn to read and write files with java.nio.file, and use newer language features such as records, sealed types and pattern matching to keep code short.
import java.nio.file.*;
import java.io.*;
import java.util.*;
import java.util.stream.*;
Path path = Path.of("data", "notes.txt");
// Whole-file operations
Files.writeString(path, "first line\nsecond line\n");
String all = Files.readString(path);
List<String> lines = Files.readAllLines(path);
// Append instead of overwrite
Files.writeString(path, "third line\n", StandardOpenOption.APPEND);
// Stream a large file lazily — never loads it all into memory
try (Stream<String> stream = Files.lines(path)) {
long errors = stream.filter(l -> l.contains("ERROR")).count();
System.out.println(errors);
}
// Buffered writer for many small writes
try (BufferedWriter w = Files.newBufferedWriter(Path.of("out.csv"))) {
w.write("id,name");
w.newLine();
w.write("1,Ada");
w.newLine();
}
// Paths and directories
Files.createDirectories(Path.of("data/archive"));
System.out.println(Files.exists(path) + " " + Files.size(path));
try (Stream<Path> walk = Files.walk(Path.of("data"))) {
walk.filter(Files::isRegularFile).forEach(System.out::println);
}// Records: immutable data carriers in one line.
// You get constructor, accessors, equals, hashCode and toString for free.
record Point(int x, int y) {
// Compact constructor for validation
Point {
if (x < 0 || y < 0) throw new IllegalArgumentException("negative coordinate");
}
// Extra methods are allowed
double distanceTo(Point other) {
return Math.hypot(x - other.x, y - other.y);
}
static Point origin() { return new Point(0, 0); }
}
var a = new Point(3, 4);
System.out.println(a); // Point[x=3, y=4]
System.out.println(a.distanceTo(Point.origin())); // 5.0
System.out.println(a.equals(new Point(3, 4))); // true — value equality
// Sealed types: a closed set of subtypes the compiler knows about
sealed interface Event permits Click, KeyPress, Scroll {}
record Click(int x, int y) implements Event {}
record KeyPress(char key) implements Event {}
record Scroll(int amount) implements Event {}// Pattern matching over a sealed hierarchy — exhaustive, no default needed.
static String describe(Event e) {
return switch (e) {
case Click(int x, int y) when x == y -> "diagonal click at " + x;
case Click(int x, int y) -> "click at " + x + "," + y;
case KeyPress(char k) -> "key " + k;
case Scroll s -> "scrolled " + s.amount();
};
}
// instanceof pattern removes the cast
static int length(Object o) {
if (o instanceof String s) return s.length(); // s is in scope, already cast
if (o instanceof Collection<?> c) return c.size();
return -1;
}
// Text blocks for multi-line strings
String query = """
SELECT id, name
FROM users
WHERE active = true
ORDER BY name
""";
// var for local type inference (the type is still static, just inferred)
var counts = new HashMap<String, Integer>();
for (var entry : counts.entrySet()) { /* ... */ }Finished reading?Mark it done to track your progress.
Tip: use the ← and → keys to move between lessons.