📁 I/O, Files & Networking · Intermediate

Path & Files (NIO.2) in Java

Path.of, Files.readString, readAllLines, lines, write, exists, walk.

🧩 The mysteryYou write Path.of("data", "in.txt") and expect Java to complain if the file is missing. It doesn't say a word. Is it broken, or does a Path not care?

An address, not a house

A **Path is like an address written on paper: writing it down doesn't check the house exists**. Path.of("data", "in.txt") never touches the disk. The **Files** class does the real work — e.g. Files.exists(path) checks.

Path anatomy

**getFileName() is the last element, getParent() everything before it, and getNameCount()** counts the names — the root / is not a name.

Path p = Path.of("/home/ana/docs/report.txt");
p.getFileName();  // report.txt
p.getParent();    // /home/ana/docs
p.getNameCount(); // 4
🔮 Predict it

Taking a path apart

What does this print?

Path p = Path.of("/srv/app/logs/today.log");
System.out.println(p.getFileName());
System.out.println(
    p.getParent().getFileName());
System.out.println(p.getNameCount());
  1. today.log logs 4
  2. today logs 4
  3. today.log /srv/app/logs 5
Show the answer

today.log, then the parent's last name logs, then 4 names: srv, app, logs, today.log.

resolve and normalize

**resolve(other) joins a relative path onto a base. normalize()** removes . and .. segments by stepping up for each ...

Path base = Path.of("/app/conf");
base.resolve("../logs/a.log");
// /app/conf/../logs/a.log
// normalize() → /app/logs/a.log
🔮 Predict it

Climbing the tree

What does this print?

Path p = Path.of("/data/in")
    .resolve("./../out/x.csv")
    .normalize();
System.out.println(p);
  1. /data/in/./../out/x.csv
  2. /data/out/x.csv
  3. /out/x.csv
Show the answer

/data/out/x.csv — . means "here" and disappears; .. steps up from in to data.

The Files menu

**readString(p): whole file → one String. readAllLines(p)**: → List<String>. **lines(p): a lazy** Stream<String> of lines. **walk(dir)**: a lazy Stream<Path> of a whole directory tree. Writing: **writeString / write**.

Path p = Path.of("notes", "todo.txt");
Files.writeString(p, "buy milk\n");
List<String> all = Files.readAllLines(p);
⚠️ The trap

Loading 20 GB into memory

readString and readAllLines load everything at once — on a 20 GB log that's an OutOfMemoryError. **Files.lines** reads lazily, line by line, so memory stays small (and it must be closed).

try (var lines = Files.lines(p)) {
    long errors = lines
        .filter(l -> l.contains("ERROR"))
        .count();
}
💼 In the real world

Files in real services

Config loaders, report generators and log processors all live on Path and Files. Choosing lines over readAllLines for big inputs, and normalize() + checks for user-supplied paths (to block ../../etc/passwd tricks), are everyday production concerns.

Key takeaways

  1. Path.of("data", "in.txt") just builds a path
  2. Files.readString / readAllLines load the whole file
  3. Files.lines and Files.walk return lazy Streams
  4. resolve() joins paths; normalize() removes . and ..
🤯 Did you know?

NIO.2 (Path and Files) arrived in Java 7; Path.of itself was only added in Java 11 — before that everyone wrote Paths.get(...).

Practice questions

What does this print?

Path p = Path.of("/home/ana/docs/report.txt");
System.out.println(p.getFileName());
System.out.println(p.getParent().getFileName());
System.out.println(p.getNameCount());
  1. report.txt docs 4
  2. report docs 4
  3. report.txt /home/ana/docs 5
  4. report.txt ana 3
Check your answer

report.txt docs 4. getFileName is the last element; the parent's last element is docs. The root / isn't a name, so there are 4 names: home, ana, docs, report.txt.

You must count the ERROR lines in a 20 GB log file. Which approach is best?

  1. Files.readAllLines(p) then loop
  2. Files.readString(p).split("\n")
  3. try (var lines = Files.lines(p)) { lines.filter(…).count(); }
  4. new String(Files.readAllBytes(p))
Check your answer

try (var lines = Files.lines(p)) { lines.filter(…).count(); }. Files.lines reads the file lazily, line by line, so memory stays small. The other options load all 20 GB into memory first.

Next: Files.lines holds a file open. Who closes it — and what if you forget?