Skip to content
Reading and Writing Files
step 1/6

Reading — step 1 of 6

Learn

~2 min readFiles and I/O

Java's modern file I/O lives in java.nio.file. The classic java.io.File works but is more verbose and less ergonomic. Use NIO for new code.

Reading a small file

import java.nio.file.*;

String contents = Files.readString(Path.of("data.txt"));
// Or:
List<String> lines = Files.readAllLines(Path.of("data.txt"));

Files.readString (Java 11+) reads the entire file as one string. readAllLines returns each line in a list. Both load the whole file into memory — fine for small files, dangerous for huge ones.

Writing

Files.writeString(Path.of("output.txt"), "Hello, file!\n");
Files.write(Path.of("log.txt"), List.of("line 1", "line 2", "line 3"));

// Append instead of overwrite:
Files.writeString(
    Path.of("log.txt"),
    "new line\n",
    StandardOpenOption.APPEND, StandardOpenOption.CREATE
);

Streaming a large file line by line

try (Stream<String> lines = Files.lines(Path.of("huge.log"))) {
    long errors = lines
        .filter(line -> line.contains("ERROR"))
        .count();
    System.out.println("errors: " + errors);
}

Files.lines returns a Stream — values come one at a time, constant memory regardless of file size. Always wrap in try-with-resources so the underlying file handle gets closed.

Try-with-resources — the cleanup pattern

Anything that implements AutoCloseable can be auto-closed:

try (BufferedReader br = Files.newBufferedReader(Path.of("data.txt"))) {
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }
} // br automatically closed here, even on exception

Cleaner than the old try/finally { f.close() } pattern. Multiple resources separated by ; close in reverse declaration order.

Path basics

Path p = Path.of("data", "users", "alice.json");
p.toString();            // "data/users/alice.json" (or backslashes on Windows)
p.getFileName();         // "alice.json"
p.getParent();           // "data/users"
p.getNameCount();        // 3
p.resolve("backup.json"); // "data/users/backup.json" — joins paths

Path.of is the modern way; Paths.get is the older equivalent. Both work.

Common file operations

Files.exists(p);
Files.isDirectory(p);
Files.size(p);                 // bytes
Files.delete(p);                // throws if missing
Files.deleteIfExists(p);        // doesn't throw
Files.copy(src, dst, REPLACE_EXISTING);
Files.move(src, dst, REPLACE_EXISTING);
Files.createDirectories(p);     // mkdir -p
Files.list(p);                  // direct children as Stream
Files.walk(p);                   // recursive Stream

Common mistakes

  • Forgetting try-with-resources with Files.lines — the file handle leaks until GC.
  • Using Files.readAllLines on a 5GB file — OOM. Use Files.lines with a stream.
  • Hardcoding / in paths — works on *nix but breaks on Windows. Use Path.of("a", "b") to construct.
  • Catching Exception to swallow IOException — handle specifically; log or surface.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…