1. The Hook (The "Byte-Sized" Intro)
- In a Nutshell: NIO.2 (Java 7+, java.nio.file) provides modern file I/O. Path = file/directory reference (replaces File). Files class = static utility methods for all operations.
- Key methods: Files.readAllLines() (read entire file), Files.write() (write), Files.copy()/move()/delete() (operations), Files.walk() (recursive traversal), Files.lines() (lazy Stream). Always use UTF-8 explicitly! Supports symbolic links, file attributes, watch services. Preferred over File class!
Think of GPS navigation. Old File class = paper map (clunky, manual). NIO.2 Path/Files = GPS (modern, automatic, feature-rich). Both get you there, but GPS is WAY better!
2. Conceptual Clarity (The "Simple" Tier)
💡 The Analogy
- Path: Address in GPS (just location, not the actual place)
- Files: GPS operations (navigate, measure distance, find shortcuts)
- Files.walk(): Exploring entire neighborhood street-by-street
3. Technical Mastery (The "Deep Dive")
Common Files Operations
| Category | Method | Purpose |
|---|---|---|
| Reading | readAllBytes(), readAllLines(), lines() | Read files |
| Writing | write(), writeString() | Write files |
| Operations | copy(), move(), delete() | Manipulate files |
| Checks | exists(), isDirectory(), isReadable() | File info |
| Directory | list(), walk(), walkFileTree() | Traverse |
| Creation | createFile(), createDirectory() | Create |
4. Interactive & Applied Code
java
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.io.IOException;
import java.util.*;
import java.util.stream.*;
public class NIO2Demo {
public static void main(String[] args) throws Exception {
demonstratePathOperations();
demonstrateFileReading();
demonstrateFileWriting();
demonstrateFileOperations();
demonstrateDirectoryOperations();
}
// Path operations
static void demonstratePathOperations() {
System.out.println("=== PATH OPERATIONS ===");
Path path = Paths.get("folder", "subfolder", "file.txt");
System.out.println("Path: " + path);
System.out.println("File name: " + path.getFileName());
System.out.println("Parent: " + path.getParent());
System.out.println("Root: " + path.getRoot());
System.out.println("Absolute: " + path.toAbsolutePath());
// Path manipulation
Path path2 = Paths.get("other.txt");
Path relative = path.relativize(path2);
System.out.println("Relative path: " + relative);
}
// Reading files (multiple ways)
static void demonstrateFileReading() throws Exception {
System.out.println("\n=== FILE READING ===");
Path file = Paths.get("sample.txt");
// Create sample file
Files.writeString(file, "Line 1\nLine 2\nLine 3\n",
StandardCharsets.UTF_8);
// Method 1: Read all lines into List
List<String> lines = Files.readAllLines(file, StandardCharsets.UTF_8);
System.out.println("Read all lines: " + lines);
// Method 2: Read all bytes
byte[] bytes = Files.readAllBytes(file);
System.out.println("Read bytes: " + new String(bytes));
// Method 3: Stream lines (lazy, memory-efficient)
try (Stream<String> stream = Files.lines(file, StandardCharsets.UTF_8)) {
long count = stream.filter(line -> line.startsWith("Line")).count();
System.out.println("Lines starting with 'Line': " + count);
}
// Method 4: BufferedReader
try (var reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(" " + line);
}
}
}
// Writing files
static void demonstrateFileWriting() throws Exception {
System.out.println("\n=== FILE WRITING ===");
Path file = Paths.get("output.txt");
// Method 1: Write string (Java 11+)
Files.writeString(file, "Hello, NIO.2!\n", StandardCharsets.UTF_8);
// Method 2: Write lines
List<String> lines = Arrays.asList("Line 1", "Line 2", "Line 3");
Files.write(file, lines, StandardCharsets.UTF_8,
StandardOpenOption.APPEND); // Append mode
// Method 3: BufferedWriter
try (var writer = Files.newBufferedWriter(file, StandardCharsets.UTF_8,
StandardOpenOption.APPEND)) {
writer.write("Line 4\n");
}
System.out.println("File written: " + file);
}
// File operations
static void demonstrateFileOperations() throws Exception {
System.out.println("\n=== FILE OPERATIONS ===");
Path source = Paths.get("source.txt");
Path dest = Paths.get("dest.txt");
Path moved = Paths.get("moved.txt");
// Create source
Files.writeString(source, "Source content");
// Copy
Files.copy(source, dest, StandardCopyOption.REPLACE_EXISTING);
System.out.println("Copied: " + source + " -> " + dest);
// Move
Files.move(dest, moved, StandardCopyOption.REPLACE_EXISTING);
System.out.println("Moved: " + dest + " -> " + moved);
// Delete
Files.deleteIfExists(moved);
System.out.println("Deleted: " + moved);
// File info
if (Files.exists(source)) {
System.out.println("Size: " + Files.size(source) + " bytes");
System.out.println("Last modified: " + Files.getLastModifiedTime(source));
System.out.println("Is readable: " + Files.isReadable(source));
}
}
// Directory operations
static void demonstrateDirectoryOperations() throws Exception {
System.out.println("\n=== DIRECTORY OPERATIONS ===");
// Create directory
Path dir = Paths.get("testdir");
Files.createDirectories(dir); // Creates parent dirs if needed
// Create files in directory
Files.writeString(dir.resolve("file1.txt"), "File 1");
Files.writeString(dir.resolve("file2.txt"), "File 2");
Files.createDirectory(dir.resolve("subdir"));
// List directory (non-recursive)
System.out.println("Directory contents:");
try (Stream<Path> stream = Files.list(dir)) {
stream.forEach(path ->
System.out.println(" " + path.getFileName()));
}
// Walk directory tree (recursive)
System.out.println("\nRecursive walk:");
try (Stream<Path> stream = Files.walk(dir)) {
stream.forEach(path ->
System.out.println(" " + path));
}
// Find files (with filter)
System.out.println("\nFind .txt files:");
try (Stream<Path> stream = Files.find(dir, 10,
(path, attrs) -> path.toString().endsWith(".txt"))) {
stream.forEach(path ->
System.out.println(" " + path.getFileName()));
}
}
}5. The Comparison & Decision Layer
| Task | Old (File) | New (NIO.2) |
|---|---|---|
| Read file | FileReader + BufferedReader | Files.readAllLines() |
| Write file | FileWriter + BufferedWriter | Files.writeString() |
| Copy file | Manual byte copy | Files.copy() |
| List directory | file.listFiles() | Files.list() (Stream) |
| Recursive walk | Manual recursion | Files.walk() |
6. The "Interview Corner" (The Edge)
The "Killer" Interview Question: "What's the difference between Files.list() and Files.walk()?" Answer:
- Files.list(): Shallow (one level only, direct children)
- Files.walk(): Deep (recursive, entire subtree)
java
// Directory structure:
// dir/
// file1.txt
// subdir/
// file2.txt
// list() returns: file1.txt, subdir (2 items)
Files.list(Paths.get("dir")).forEach(System.out::println);
// walk() returns: dir, file1.txt, subdir, file2.txt (4 items)
Files.walk(Paths.get("dir")).forEach(System.out::println);Pro-Tip: Always close Streams from Files methods:
java
// ❌ RESOURCE LEAK
Files.list(dir).forEach(System.out::println);
// Stream not closed → file handles leak!
// ✅ CORRECT
try (Stream<Path> stream = Files.list(dir)) {
stream.forEach(System.out::println);
} // Auto-closedFiles.list(), Files.walk(), Files.lines() return AutoCloseable streams!