V
Vel·ToolKit
Simple · Fast · Ready to use
EN
Chapter 18 of 20

File & IO

Path, Files (NIO.2), byte streams vs char streams, directory traversal

Where IO Is Used in Java

Almost every non-pure-memory program does IO: reading config / writing logs / handling uploads / exporting CSV / parsing big data. Java's IO API comes in two generations: the legacy java.io (File/FileInputStream/Reader) and NIO.2 java.nio.file (Path/Files); the latter has been preferred since Java 7 — more concise, better error messages, more robust cross-platform path handling.

  • Small files, read/write at once: Files.readString / writeString
  • Large files, line-by-line streaming: Files.lines (with try-with-resources)
  • Binary read/write: Files.newInputStream / newOutputStream + BufferedStream
  • Directory traversal: Files.walk / Files.list
  • Char vs byte: text uses Reader/Writer (handles encoding automatically), binary uses InputStream/OutputStream

Path: Cross-Platform Paths

Path is the core class of java.nio.file, representing a filesystem path. Path.of(...) is the creation entry point and automatically handles Windows / Linux separator differences.

// PathDemo.java
import java.nio.file.Path;
import java.nio.file.Paths;

public class PathDemo {
    public static void main(String[] args) {
        Path p = Path.of("data", "users", "list.json");
        System.out.println(p);                  // data/users/list.json (or data\\users\\list.json on Windows)
        System.out.println(p.toAbsolutePath());
        System.out.println(p.getFileName());    // list.json
        System.out.println(p.getParent());       // data/users
        System.out.println(p.getName(0));        // data
        System.out.println(p.getNameCount());    // 3

        // join
        Path base = Path.of("/var/log");
        Path full = base.resolve("app.log");
        System.out.println(full);                // /var/log/app.log

        // normalize (eliminate . and ..)
        System.out.println(Path.of("a/./b/../c").normalize()); // a/c
    }
}

Small Files: Read/Write at Once

Good for config files, templates, small JSON — usually a few KB ~ a few MB. Files.readString / writeString use UTF-8 automatically (you can also pass a charset explicitly).

// SmallFile.java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;

public class SmallFile {
    public static void main(String[] args) throws IOException {
        Path p = Path.of("hello.txt");

        // write (overwrites by default)
        Files.writeString(p, "hello\nworld\n");

        // append
        Files.writeString(p, "more\n", StandardOpenOption.APPEND);

        // read as string
        String content = Files.readString(p);
        System.out.println(content);

        // read all bytes (binary)
        byte[] bytes = Files.readAllBytes(p);
        System.out.println("size=" + bytes.length);

        Files.delete(p);
    }
}

Large Files: Line-by-Line Streaming

Use case: analyzing a few-hundred-MB log file, reading CSV data. Files.lines returns a Stream<String> that reads **lazily** line by line; with try-with-resources it closes the underlying file handle automatically.

// BigFile.java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;

public class BigFile {
    public static void main(String[] args) throws IOException {
        Path p = Files.createTempFile("log", ".txt");
        Files.writeString(p, """
            INFO  user login id=1
            ERROR db down
            INFO  user login id=2
            WARN  slow query 1200ms
            ERROR connection reset
            """);

        // stream line by line, auto-close the stream (key: try-with-resources)
        try (Stream<String> lines = Files.lines(p)) {
            long errCount = lines
                .filter(line -> line.startsWith("ERROR"))
                .peek(System.out::println)
                .count();
            System.out.println("errors = " + errCount);
        }

        Files.delete(p);
    }
}

Byte Streams vs Char Streams

InputStream / OutputStream handle bytes, good for binary (images, PDFs, archives); Reader / Writer handle characters, decoding bytes by the specified encoding, good for text. BufferedReader / BufferedWriter add buffering and speed things up substantially.

// Streams.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;

public class Streams {
    public static void main(String[] args) throws IOException {
        Path p = Files.createTempFile("demo", ".txt");
        Files.writeString(p, "line1\nline2\nline3\n");

        // byte stream: read raw bytes
        try (InputStream in = Files.newInputStream(p)) {
            int b;
            int count = 0;
            while ((b = in.read()) != -1) count++;
            System.out.println("bytes = " + count);
        }

        // char stream + buffering: efficient line reading
        try (BufferedReader reader = Files.newBufferedReader(p, StandardCharsets.UTF_8)) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        }

        Files.delete(p);
    }
}

Directory and File Operations

// FileOps.java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;

public class FileOps {
    public static void main(String[] args) throws IOException {
        Path dir = Files.createTempDirectory("demo-");
        Path src = dir.resolve("a.txt");
        Path dst = dir.resolve("b.txt");

        Files.writeString(src, "hello");
        System.out.println(Files.exists(src));         // true
        System.out.println(Files.size(src));            // 5

        // copy
        Files.copy(src, dst, StandardCopyOption.REPLACE_EXISTING);

        // move / rename
        Path moved = dir.resolve("c.txt");
        Files.move(dst, moved, StandardCopyOption.REPLACE_EXISTING);

        // delete
        Files.delete(src);
        Files.delete(moved);
        Files.delete(dir);
    }
}

Directory Traversal: Files.walk / Files.list

Use cases: scan all .java files in a directory to count lines of code, find files with a specific suffix, build an index. walk recurses, list looks at one level only. Both return Stream<Path> and **must use try-with-resources**.

// WalkDemo.java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;

public class WalkDemo {
    public static void main(String[] args) throws IOException {
        Path root = Path.of(".");

        // list the current directory only
        try (Stream<Path> top = Files.list(root)) {
            top.forEach(System.out::println);
        }

        System.out.println("--- recursive ---");

        // recursively find all .java files
        try (Stream<Path> walk = Files.walk(root)) {
            walk.filter(Files::isRegularFile)
                .filter(p -> p.toString().endsWith(".java"))
                .limit(10)
                .forEach(System.out::println);
        }
    }
}

Download/Upload: Classic Stream Writing

Use cases: HTTP download, writing network bytes to disk, reading/writing archives. InputStream.transferTo (Java 9+) does it in one line.

// Transfer.java
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;

public class Transfer {
    public static void main(String[] args) throws IOException {
        // simulate a network stream
        InputStream in = new ByteArrayInputStream("streaming payload".getBytes());
        Path dst = Files.createTempFile("out", ".bin");

        try (InputStream src = in; OutputStream out = Files.newOutputStream(dst)) {
            long bytes = src.transferTo(out);   // Java 9+
            System.out.println("transferred " + bytes + " bytes");
        }

        System.out.println(Files.readString(dst));
        Files.delete(dst);
    }
}