Java 7 introduced NIO.2 (New I/O 2) in the java.nio.file package to address the limitations of the classic java.io.File class. NIO.2 provides cross-platform file manipulation, atomic file operations, deep directory trees traversal, and native integration with Java Streams.
The Path interface is the modern replacement for java.io.File. You create a Path using Path.of() (Java 11+) or Paths.get():
import java.nio.file.Path;
import java.nio.file.Paths;
public class PathBasics {
public static void main(String[] args) {
// Modern Java 11+ Path factory
Path p1 = Path.of("data", "reports", "annual.csv");
System.out.println("File Name: " + p1.getFileName());
System.out.println("Parent Directory: " + p1.getParent());
System.out.println("Root: " + p1.getRoot());
System.out.println("Subpath (0, 2): " + p1.subpath(0, 2));
System.out.println("Is Absolute: " + p1.isAbsolute());
// Normalizing redundant elements (e.g., ../ or ./)
Path complex = Path.of("data/./temp/../reports/2026.txt");
System.out.println("Normalized: " + complex.normalize());
}
}
Modern Java eliminates boilerplate with Files.readString() and Files.writeString():
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.io.IOException;
public class ModernFileOps {
public static void main(String[] args) throws IOException {
Path path = Path.of("welcome.txt");
// Write entire string in one clean method call
Files.writeString(path, "Hello from Veewom Java 21!\nModern NIO.2 rocks.\n");
// Append text easily
Files.writeString(path, "Appended line.\n", StandardOpenOption.APPEND);
// Read entire content in one method call
String content = Files.readString(path);
System.out.println("File Content:\n" + content);
}
}
For large multi-gigabyte log files, Files.lines() generates a lazy Stream<String> that reads data line-by-line without exhausting heap memory:
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.IOException;
import java.util.stream.Stream;
public class StreamLargeFile {
public static void main(String[] args) {
Path logPath = Path.of("production.log");
// Files.lines() must be closed via try-with-resources to free OS handle
try (Stream lines = Files.lines(logPath)) {
long errorCount = lines
.filter(line -> line.contains("ERROR") || line.contains("FATAL"))
.peek(System.out::println)
.count();
System.out.println("Total critical errors detected: " + errorCount);
} catch (IOException e) {
System.err.println("Failed to stream log file: " + e.getMessage());
}
}
}
Files.walk() recursively traverses an entire directory hierarchy returning a Stream of Paths:
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.IOException;
import java.util.stream.Stream;
public class DirectoryCrawler {
public static void main(String[] args) throws IOException {
Path rootDir = Path.of("src");
try (Stream stream = Files.walk(rootDir, 5)) {
stream.filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(".java"))
.forEach(p -> System.out.println("Java Source: " + p.getFileName()));
}
}
}
| Technique | File Size | Heap Behavior | Risk Level |
|---|---|---|---|
Files.readAllLines(path) | 2.5 GB Log | Allocates ArrayList containing all 2.5 GB of text in heap simultaneously | High: Triggers java.lang.OutOfMemoryError |
Files.lines(path) | 2.5 GB Log | Streams 1 line at a time; garbage collector reclaims discarded line immediately | Zero: Memory footprint remains below 32 MB |
java.io.File and java.nio.file.Path?
File is an older class with poorly designed boolean return methods (e.g., file.delete() returns false with no explanation when it fails). Path throws informative descriptive exceptions (like NoSuchFileException, DirectoryNotEmptyException) and integrates natively with modern OS filesystem capabilities and Java Streams.Accelerate your Java mastery by studying these tightly connected core tutorials:
Understanding original Byte and Character streams.
Processing Files.lines() streams functionally.
Classic buffered line reading techniques.
Catching NoSuchFileException and IOException.