iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Files (NIO)

Files (java.nio.file) is the modern Java I/O API: Path objects, atomic moves, walking trees, reading lines as a Stream. Use it for any file work you would have written with FileInputStream in 2010 — cleaner, faster, and harder to leak handles.

Read, write, walk, watch, atomic move

EXAMPLE
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.util.*;
import java.util.stream.*;

public class FilesDemo {

    public static void main(String[] args) throws Exception {
        // 1) Path basics
        Path home   = Paths.get(System.getProperty("user.home"));
        Path data   = home.resolve("shop").resolve("data");
        Path out    = data.resolve("orders.csv");

        Files.createDirectories(data);                   // mkdir -p

        // 2) Read / write — small files
        Files.writeString(out, "id,total\n1,100\n", StandardCharsets.UTF_8);
        String body = Files.readString(out);
        List<String> lines = Files.readAllLines(out);

        // 3) Stream large files line-by-line — close in try-with-resources
        try (Stream<String> stream = Files.lines(out)) {
            long count = stream.skip(1).count();
            System.out.println("rows: " + count);
        }

        // 4) Atomic rename — write to .tmp, then move
        Path tmp = out.resolveSibling(out.getFileName() + ".tmp");
        Files.writeString(tmp, "id,total\n1,100\n2,200\n");
        Files.move(tmp, out, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);

        // 5) Walk a directory tree
        try (Stream<Path> walk = Files.walk(data)) {
            walk.filter(Files::isRegularFile)
                .filter(p -> p.toString().endsWith(".csv"))
                .forEach(System.out::println);
        }

        // 6) Find recently modified files
        try (Stream<Path> walk = Files.walk(data)) {
            walk.filter(Files::isRegularFile)
                .sorted(Comparator.comparing(p -> {
                    try { return Files.getLastModifiedTime(p).toInstant(); }
                    catch (IOException e) { return java.time.Instant.EPOCH; }
                }, Comparator.reverseOrder()))
                .limit(10)
                .forEach(System.out::println);
        }

        // 7) Watch a directory for changes
        WatchService watcher = FileSystems.getDefault().newWatchService();
        data.register(watcher, StandardWatchEventKinds.ENTRY_CREATE,
                                StandardWatchEventKinds.ENTRY_MODIFY);
        new Thread(() -> {
            while (true) {
                try {
                    WatchKey key = watcher.take();      // blocks
                    for (WatchEvent<?> e : key.pollEvents()) {
                        System.out.println(e.kind() + " " + e.context());
                    }
                    key.reset();
                } catch (InterruptedException ex) { return; }
            }
        }, "watcher").start();

        // 8) Copy + delete recursively
        Path target = data.resolveSibling("data-backup");
        try (Stream<Path> walk = Files.walk(data)) {
            walk.forEach(src -> {
                try {
                    Path rel = data.relativize(src);
                    Path dst = target.resolve(rel);
                    if (Files.isDirectory(src)) Files.createDirectories(dst);
                    else Files.copy(src, dst, StandardCopyOption.REPLACE_EXISTING);
                } catch (IOException e) { throw new UncheckedIOException(e); }
            });
        }

        try (Stream<Path> walk = Files.walk(target)) {
            walk.sorted(Comparator.reverseOrder()).forEach(p -> {
                try { Files.delete(p); }
                catch (IOException e) { throw new UncheckedIOException(e); }
            });
        }

        // 9) File attributes
        BasicFileAttributes attrs = Files.readAttributes(out, BasicFileAttributes.class);
        System.out.println("size: " + attrs.size());
        System.out.println("created: " + attrs.creationTime());

        // 10) Streams + paths together — count lines across all CSVs
        try (Stream<Path> walk = Files.walk(data)) {
            long total = walk
                .filter(Files::isRegularFile)
                .filter(p -> p.toString().endsWith(".csv"))
                .mapToLong(p -> {
                    try (Stream<String> ls = Files.lines(p)) { return ls.count(); }
                    catch (IOException e) { return 0; }
                })
                .sum();
            System.out.println("total lines: " + total);
        }

        // 11) Pitfalls
        // - Forgetting to close Files.lines() / Files.walk() (keep file handles)
        // - Files.move() across filesystems may fail; use COPY + DELETE for cross-fs
        // - ATOMIC_MOVE not supported on every filesystem; fall back gracefully
        // - readAllBytes on a 10GB file -> OOM; stream instead
        // - Charset omitted -> system default; ALWAYS pass UTF_8 explicitly
    }
}

Why it matters

Always close `Files.lines()` and `Files.walk()` with try-with-resources. They hold OS file handles until closed, and the JVM does not eagerly reclaim them. The Streams iterator pattern looks lazy and clean, but the lazy resource is real — leak enough of them and `ulimit -n` becomes your bug.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
import java.nio.file.*;
List<String> lines = Files.readAllLines(Path.of("input.txt"));
Files.writeString(Path.of("out.txt"), "hi");
Try it Yourself »

Discussion

Loading…