Java NIO.2 Path and Files API: Modern File I/O
The NIO.2 API (java.nio.file), introduced in Java 7, replaces the aging java.io.File class with two cleaner, more capable abstractions: Path (an immutable representation of a file system path) and Files (a utility class with static methods for all common file operations). Java 11 added Files.readString() and Files.writeString() for one-liner file I/O, making NIO.2 even more ergonomic.
Contents
Path: Creating, Resolving, Relativizing
Path.of()Paths.get()Pathresolve()cdrelativize()normalize()...toAbsolutePath()
import java.nio.file.*;
// Creating paths
Path p1 = Path.of("/home/user/docs/report.txt"); // absolute
Path p2 = Path.of("src", "main", "java", "App.java"); // relative, OS separator
Path p3 = Path.of("."); // current directory
// Path components
System.out.println(p1.getFileName()); // report.txt
System.out.println(p1.getParent()); // /home/user/docs
System.out.println(p1.getRoot()); // / (null on Windows relative paths)
System.out.println(p1.getNameCount()); // 4
System.out.println(p1.getName(2)); // docs
// resolve — append a path (like cd)
Path dir = Path.of("/home/user");
Path file = dir.resolve("docs/report.txt"); // /home/user/docs/report.txt
Path abs = dir.resolve(Path.of("/etc/hosts")); // /etc/hosts (absolute overrides)
// resolveSibling — resolve relative to parent
Path sibling = p1.resolveSibling("summary.txt"); // /home/user/docs/summary.txt
// relativize — compute relative path from one to another
Path from = Path.of("/home/user");
Path to = Path.of("/home/user/docs/report.txt");
Path rel = from.relativize(to); // docs/report.txt
// normalize — remove . and .. components
Path messy = Path.of("/home/user/../user/./docs/report.txt");
System.out.println(messy.normalize()); // /home/user/docs/report.txt
// toAbsolutePath — make relative path absolute (uses current directory)
Path abs2 = Path.of("config.properties").toAbsolutePath();
// toRealPath — resolve symlinks, check existence
// Path real = Path.of("link").toRealPath(); // throws NoSuchFileException if missing
// startsWith / endsWith
System.out.println(p1.startsWith("/home/user")); // true
System.out.println(p1.endsWith("report.txt")); // true
Reading and Writing Files
Files.readString()Files.readAllBytes()Files.readAllLines()List<String>Files.lines()Files.writeString()Files.write()StandardOpenOption.APPENDFilesIOException
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
Path file = Path.of("data.txt");
// Read entire file as String (Java 11+) — simplest option
String content = Files.readString(file);
String contentUtf8 = Files.readString(file, StandardCharsets.UTF_8);
// Read all lines as List<String>
List<String> lines = Files.readAllLines(file);
// Read as bytes
byte[] bytes = Files.readAllBytes(file);
// Stream lines lazily (good for large files — doesn't load all at once)
try (Stream<String> stream = Files.lines(file)) {
long count = stream.filter(l -> l.contains("ERROR")).count();
}
// Write entire file (Java 11+)
Files.writeString(file, "Hello, World!\n");
Files.writeString(file, "Append me\n", StandardOpenOption.APPEND);
// Write all lines
Files.write(file, List.of("line1", "line2", "line3"));
Files.write(file, List.of("more"), StandardOpenOption.APPEND);
// Buffered reader/writer for streaming I/O
try (var reader = Files.newBufferedReader(file)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
try (var writer = Files.newBufferedWriter(file, StandardOpenOption.CREATE)) {
writer.write("line 1");
writer.newLine();
writer.write("line 2");
}
// Temporary files
Path tempFile = Files.createTempFile("prefix-", ".tmp");
Path tempDir = Files.createTempDirectory("myapp-");
Copy, Move, and Delete
Files.copy()REPLACE_EXISTINGFiles.move()Files.delete()NoSuchFileExceptionFiles.deleteIfExists()
Path src = Path.of("src/data.txt");
Path dest = Path.of("dest/data.txt");
// Copy — fails if destination exists by default
Files.copy(src, dest);
// Copy with options
Files.copy(src, dest,
StandardCopyOption.REPLACE_EXISTING, // overwrite if exists
StandardCopyOption.COPY_ATTRIBUTES); // preserve timestamps etc.
// Copy from/to InputStream (useful for resources, downloads)
try (InputStream in = SomeApi.getStream()) {
Files.copy(in, dest, StandardCopyOption.REPLACE_EXISTING);
}
try (OutputStream out = new FileOutputStream("out.bin")) {
Files.copy(src, out);
}
// Move (rename) — atomic on same filesystem
Files.move(src, dest, StandardCopyOption.REPLACE_EXISTING);
// ATOMIC_MOVE — platform-dependent, throws AtomicMoveNotSupportedException if unavailable
Files.move(src, dest, StandardCopyOption.ATOMIC_MOVE);
// Delete
Files.delete(dest); // throws NoSuchFileException if missing
Files.deleteIfExists(dest); // no exception if missing
// Delete a directory tree
Files.walk(Path.of("target"))
.sorted(Comparator.reverseOrder()) // delete children before parents
.forEach(p -> {
try { Files.delete(p); } catch (IOException e) { throw new RuntimeException(e); }
});
// Create directories
Files.createDirectory(Path.of("newdir")); // parent must exist
Files.createDirectories(Path.of("a/b/c/d")); // creates all missing parents
Walking and Finding Files
Files.walk()Stream<Path>Files.find()BiPredicate<Path, BasicFileAttributes>lsFiles.list()
Path root = Path.of("/home/user/projects");
// Walk — depth-first stream of all paths (files + directories)
try (Stream<Path> walk = Files.walk(root)) {
walk.filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(".java"))
.forEach(System.out::println);
}
// Walk with max depth
try (Stream<Path> shallow = Files.walk(root, 2)) {
// only goes 2 levels deep
}
// find — walk with a BiPredicate (more efficient than walk + filter)
try (Stream<Path> found = Files.find(root, Integer.MAX_VALUE,
(path, attrs) -> attrs.isRegularFile()
&& path.toString().endsWith(".java")
&& attrs.size() > 1024)) {
found.forEach(System.out::println);
}
// list — non-recursive listing of a directory (like ls)
try (Stream<Path> entries = Files.list(root)) {
entries.filter(Files::isDirectory)
.forEach(System.out::println);
}
// FileVisitor — full control over traversal
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
System.out.println("File: " + file + " (" + attrs.size() + " bytes)");
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
if (dir.getFileName().toString().equals(".git")) {
return FileVisitResult.SKIP_SUBTREE; // skip .git directories
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) {
System.err.println("Failed to visit: " + file + " - " + exc);
return FileVisitResult.CONTINUE; // continue despite error
}
});
File Attributes and Metadata
The FilesFiles.size()Files.isRegularFile()Files.isDirectory()Files.isReadable()Files.isWritable()Files.readAttributes(path, BasicFileAttributes.class)PosixFileAttributesFiles.getLastModifiedTime()
import java.nio.file.attribute.*;
Path file = Path.of("report.txt");
// Quick checks
boolean exists = Files.exists(file);
boolean isFile = Files.isRegularFile(file);
boolean isDir = Files.isDirectory(file);
boolean readable = Files.isReadable(file);
boolean writable = Files.isWritable(file);
boolean hidden = Files.isHidden(file);
long size = Files.size(file); // throws if not exists
// Basic attributes — all at once (single OS call)
BasicFileAttributes attrs = Files.readAttributes(file, BasicFileAttributes.class);
System.out.println("Size: " + attrs.size());
System.out.println("Created: " + attrs.creationTime());
System.out.println("Modified: " + attrs.lastModifiedTime());
System.out.println("Accessed: " + attrs.lastAccessTime());
System.out.println("Is symlink: " + attrs.isSymbolicLink());
// Set modification time
Files.setLastModifiedTime(file, FileTime.fromMillis(System.currentTimeMillis()));
// POSIX attributes (Linux/macOS) — owner, group, permissions
if (FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) {
PosixFileAttributes posix = Files.readAttributes(file, PosixFileAttributes.class);
System.out.println("Owner: " + posix.owner().getName());
System.out.println("Group: " + posix.group().getName());
System.out.println("Perms: " + PosixFilePermissions.toString(posix.permissions()));
// e.g., "rw-r--r--"
// Set permissions
Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-rw-r--");
Files.setPosixFilePermissions(file, perms);
}
// User-defined attributes (extended attributes)
UserDefinedFileAttributeView view =
Files.getFileAttributeView(file, UserDefinedFileAttributeView.class);
ByteBuffer buf = ByteBuffer.allocate(64);
view.write("myapp.checksum", ByteBuffer.wrap("sha256:abc123".getBytes()));
buf.clear();
view.read("myapp.checksum", buf);
buf.flip();
System.out.println(StandardCharsets.UTF_8.decode(buf)); // sha256:abc123
PathFilesjava.io.FileFilePath.toFile()File.toPath()