NIO.2 Files — Path, Files Utility Methods, walk, find, lines, and File Operations
In this tutorial, you will learn about NIO.2 Files. We cover key concepts, practical examples, and best practices to help you master this topic.
Java NIO.2 (introduced in Java 7) provides a modern file system API centered on the Path class and Files utility, replacing much of java.io. Unlike the java.io.File class (which had many design flaws — non-atomic operations, missing symlink support, and boolean return values instead of exceptions), NIO.2 is file-system-agnostic, supports symbolic links, and uses proper Exception Handling.
What You'll Learn
- Path vs File, creating and resolving paths
- Files utility: read, write, copy, move, delete
- Walking directory trees with walk and find
- Reading all lines and streaming lines
Why It Matters
NIO.2 is the standard file API for modern Java. java.io.File is effectively deprecated. NIO.2 is faster, more reliable, and supports features like file attributes, watch services, and directory streaming.
Real-World Use
File indexing services, log file monitoring, backup utilities, and build tools all use NIO.2. Spring Boot uses it for classpath scanning and file watching.
The Path Interface
Path path = Path.of("/home/user/docs/file.txt");
Path relative = Path.of("docs", "file.txt");
// Parts of a path
System.out.println(path.getFileName()); // file.txt
System.out.println(path.getParent()); // /home/user/docs
System.out.println(path.getRoot()); // /
System.out.println(path.getNameCount()); // 4
System.out.println(path.getName(0)); // home
// Normalize
Path normalized = Path.of("/home/./docs/../docs/file.txt").normalize();
// /home/docs/file.txt
// Resolve
Path base = Path.of("/home/user");
Path full = base.resolve("docs/file.txt");
// /home/user/docs/file.txt
// Relativize
Path from = Path.of("/a/b/c");
Path to = Path.of("/a/b/d/e");
Path relativePath = from.relativize(to); // ../d/e
Files Utility Methods
Reading and Writing Files
// Read all bytes
byte[] data = Files.readAllBytes(Path.of("file.bin"));
// Read all lines
List<String> lines = Files.readAllLines(Path.of("file.txt"), StandardCharsets.UTF_8);
// Write bytes
Files.write(Path.of("output.bin"), data);
// Write lines
Files.write(Path.of("output.txt"), lines, StandardCharsets.UTF_8);
Reading with Streams
// Stream lines (lazy — doesn't load entire file into memory)
try (Stream<String> lines = Files.lines(Path.of("large.txt"), StandardCharsets.UTF_8)) {
lines.filter(line -> line.contains("error"))
.forEach(System.out::println);
}
Copy, Move, Delete
// Copy
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
Files.copy(source, target, StandardCopyOption.COPY_ATTRIBUTES);
// Move
Files.move(source, target, StandardCopyOption.ATOMIC_MOVE);
// Delete
Files.delete(path); // throws NoSuchFileException if missing
boolean deleted = Files.deleteIfExists(path); // returns false if missing
Walking Directory Trees
walk()
try (Stream<Path> stream = Files.walk(Path.of("/home/user/projects"))) {
stream.filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(".java"))
.forEach(System.out::println);
}
find()
More efficient — combines walk with predicate:
try (Stream<Path> stream = Files.find(
Path.of("/home/user"),
Integer.MAX_VALUE,
(path, attrs) -> attrs.isRegularFile() && path.toString().endsWith(".log"))) {
stream.forEach(System.out::println);
}
File Attributes
Path file = Path.of("file.txt");
boolean exists = Files.exists(file);
boolean isDir = Files.isDirectory(file);
boolean isRegular = Files.isRegularFile(file);
boolean isSymlink = Files.isSymbolicLink(file);
long size = Files.size(file);
FileTime lastModified = Files.getLastModifiedTime(file);
// Basic file attributes
BasicFileAttributes attrs = Files.readAttributes(file, BasicFileAttributes.class);
attrs.creationTime();
attrs.lastAccessTime();
attrs.isSymbolicLink();
attrs.size();
Temporary Files and Directories
Path tempFile = Files.createTempFile("prefix", ".txt");
Path tempDir = Files.createTempDirectory("app-");
// Delete on exit
tempFile.toFile().deleteOnExit();
Common Mistakes
- Using
Fileinstead ofPath.java.io.Fileis legacy. Always preferjava.nio.file.PathandFilesfor new code. - Forgetting to close streams from
Files.walk()andFiles.lines(). These returnStreamobjects that hold file handles. Always use try-with-resources. - Not specifying encoding.
Files.readAllLines()uses UTF-8 by default (Java 18+), but older versions use the platform default. Explicit encoding is safer. - Assuming
Files.delete()throws if file missing. UsedeleteIfExists()for optional files. - Using
Path.of()with null segments.Path.of("a", null, "b")throwsNullPointerException. Validate inputs.
Practice Questions
1. What is the difference between Path.of("/a/b") and Path.of("/a", "b")?
Both create a path to /a/b. The varargs version joins segments with the platform separator.
2. What does Files.walk() return?
A Stream<Path> that lazily traverses the directory tree depth-first. It must be used in a try-with-resources to close the underlying directory stream.
3. How is Files.lines() different from Files.readAllLines()?
Files.lines() returns a lazy Stream<String> that reads lines on demand. Files.readAllLines() reads the entire file into a List<String> in memory.
4. What does StandardCopyOption.ATOMIC_MOVE guarantee?
The move operation is atomic at the file system level — either it completes fully or it fails, with no intermediate state. Only works within the same file system.
5. How do you create a symbolic link with NIO.2?
Files.createSymbolicLink(linkPath, targetPath). Requires appropriate OS permissions.
Challenge Question:
Write a utility FileSearcher that takes a root directory, a glob pattern (e.g., **/*.java), and a search string. It should recursively find all matching files and print lines containing the search string with file path and line number. Use Files.find() and Files.lines(). Do not load entire files into memory.
FAQ
Mini Project
Write a program Nio2Demo.java that:
- Creates a temporary directory structure with nested files using
Files.createDirectories() - Writes content to files using
Files.writeString() - Walks the directory tree and prints all files with their sizes
- Finds all
.txtfiles larger than 1 KB usingFiles.find() - Copies a file with
REPLACE_EXISTINGand verifies they are the same withFiles.isSameFile() - Moves a file atomically within the same directory
- Deletes the temporary directory and all its contents using
Files.walk()with reverse sort - Demonstrates
Files.lines()by searching for a specific term in all text files
What's Next
NIO.2 Files handles regular file operations efficiently. For high-performance I/O and channel-based operations, NIO provides FileChannel, ByteBuffer, and SocketChannel. Lesson 42 covers NIO Channels and Buffers for non-blocking and scatter/gather I/O.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro