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);
}
}