Skip to main content
Advertisement

15.5 NIO and the Files API (Java 7~11+)

Java 7's NIO.2 (New I/O 2) introduced the java.nio.file package, making file and directory operations much easier and more powerful than the old java.io.File class.

1. Path - Representing File Paths

import java.nio.file.*;

Path path1 = Path.of("data/config.txt"); // Java 11+
Path path2 = Paths.get("data", "config.txt"); // Java 7+
Path absolute = Path.of("/home/user/data.txt");

// Path manipulation
Path parent = path1.getParent(); // data
Path fileName = path1.getFileName(); // config.txt
Path resolved = Path.of("data").resolve("config.txt"); // data/config.txt

// Normalize (remove ../ etc.)
Path messy = Path.of("data/../logs/./app.log");
Path cleaned = messy.normalize(); // logs/app.log
Path absolute2= messy.toAbsolutePath(); // absolute path

// Relative path between two paths
Path base = Path.of("/home/user");
Path target = Path.of("/home/user/docs/report.pdf");
Path rel = base.relativize(target); // docs/report.pdf

System.out.println(Path.of("a/b").startsWith("a")); // true
System.out.println(Path.of("a/b/c").endsWith("b/c")); // true

2. Files Utility Class

java.nio.file.Files provides all file operations as static methods.

Reading Files

Path path = Path.of("hello.txt");

// Read entire content as String (Java 11+)
String content = Files.readString(path);

// Read with specific charset
String eucKr = Files.readString(path, Charset.forName("EUC-KR"));

// Read all lines as List<String>
List<String> lines = Files.readAllLines(path);

// Read lazily as Stream (for large files)
try (Stream<String> lineStream = Files.lines(path)) {
lineStream
.filter(line -> !line.isBlank())
.map(String::trim)
.forEach(System.out::println);
}

// Read as byte array
byte[] bytes = Files.readAllBytes(path);

Writing Files

Path output = Path.of("output.txt");

// Write string (Java 11+, overwrites by default)
Files.writeString(output, "Hello, NIO!\nSecond line");

// Write lines
List<String> lines = List.of("First", "Second", "Third");
Files.write(output, lines);

// Append mode
Files.writeString(output, "\nAppended content", StandardOpenOption.APPEND);

File and Directory Management

Path source = Path.of("original.txt");
Path dest = Path.of("copy.txt");

// Copy (with overwrite option)
Files.copy(source, dest, StandardCopyOption.REPLACE_EXISTING);

// Move / Rename
Files.move(source, Path.of("renamed.txt"), StandardCopyOption.REPLACE_EXISTING);

// Create directories
Files.createDirectory(Path.of("newDir"));
Files.createDirectories(Path.of("a/b/c")); // Creates intermediate dirs

// Delete
Files.delete(dest);
Files.deleteIfExists(Path.of("maybe.txt")); // No exception if absent

// Temp files
Path temp = Files.createTempFile("prefix_", "_suffix.txt");
Path tempDir = Files.createTempDirectory("myapp_");

File Metadata

Path p = Path.of("data.txt");
Files.exists(p);
Files.isRegularFile(p);
Files.isDirectory(p);
Files.isReadable(p);
Files.size(p);
Files.getLastModifiedTime(p);
Files.isHidden(p);

3. Directory Traversal

Files.list - Immediate Children

try (Stream<Path> entries = Files.list(Path.of("."))) {
entries
.filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(".java"))
.forEach(System.out::println);
}

Files.walk - Recursive

try (Stream<Path> walk = Files.walk(Path.of("src"))) {
walk
.filter(p -> p.toString().endsWith(".java"))
.forEach(System.out::println);
}

// With depth limit
try (Stream<Path> walk = Files.walk(Path.of("src"), 2)) {
walk.forEach(System.out::println);
}

4. Practical Example: File Processing Utility

public class FileUtils {
// Total size of all files in a directory
public static long totalSize(Path dir) throws IOException {
try (Stream<Path> walk = Files.walk(dir)) {
return walk
.filter(Files::isRegularFile)
.mapToLong(p -> { try { return Files.size(p); } catch (IOException e) { return 0L; } })
.sum();
}
}

// Count files by extension
public static Map<String, Long> countByExtension(Path dir) throws IOException {
try (Stream<Path> walk = Files.walk(dir)) {
return walk
.filter(Files::isRegularFile)
.collect(Collectors.groupingBy(p -> {
String name = p.getFileName().toString();
int dot = name.lastIndexOf('.');
return dot == -1 ? "(none)" : name.substring(dot);
}, Collectors.counting()));
}
}
}

Pro Tip

Always close file streams with try-with-resources: Files.list(), Files.walk(), and Files.lines() all return Stream objects that hold file handles. Always use try-with-resources to prevent file handle leaks.

// ❌ Risk: stream may not be closed
Files.walk(dir).forEach(System.out::println);

// ✅ Safe: automatically closed
try (Stream<Path> s = Files.walk(dir)) {
s.forEach(System.out::println);
}

Path.of() vs Paths.get(): Use Path.of() in Java 11+. It's more concise and is the static factory method on the Path interface. Both have identical behavior.

Advertisement