Java Notes
Complete guide to Java NIO (New I/O) — Path, Files, Channels, Buffers, non-blocking I/O, WatchService, and modern file operations that replace traditional java.io approaches.
What is NIO?
NIO (New I/O) was introduced in Java 1.4 (enhanced in Java 7 as NIO.2) as a modern alternative to traditional java.io. It provides:
java.nio.file.Path— replacement forjava.io.Filejava.nio.file.Files— utility methods for file operations- Channels & Buffers — for high-performance I/O
- Non-blocking I/O — for scalable network applications
- WatchService — file system monitoring
| Traditional I/O (java.io) | NIO (java.nio): |
| File | Path |
| FileInputStream | Files.newInputStream() |
| FileOutputStream | Files.newOutputStream() |
| FileReader/Writer | Files.newBufferedReader/Writer() |
| File.listFiles() | Files.list(), Files.walk() |
| File.delete() | Files.delete() |
Path — Modern File Path Handling
Output:
| path1 | src/main/java/App.java |
| Filename | App.java |
| Parent | src/main/java |
| Root | null |
| Name count | 4 |
| Base | /home/user |
| Relative | documents/file.txt |
| Resolved | /home/user/documents/file.txt |
| Before | ./data/../config/app.properties |
| After | config/app.properties |
| From | /home/user/docs |
| To | /home/user/photos/vacation |
| Relative | ../../photos/vacation |
Files — Powerful File Operations
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.io.IOException;
import java.util.List;
public class FilesExample {
public static void main(String[] args) throws IOException {
System.out.println("=== Files Utility Class ===\n");
Path file = Path.of("demo.txt");
// --- Writing ---
System.out.println("--- Writing Files ---");
// Write string (Java 11+)
Files.writeString(file, "Hello, NIO!\nLine 2\nLine 3\n");
System.out.println("✓ writeString()");
// Write lines
Path file2 = Path.of("lines.txt");
List<String> lines = List.of("First", "Second", "Third", "Fourth");
Files.write(file2, lines, StandardCharsets.UTF_8);
System.out.println("✓ write(lines)");
// Write bytes
Path binFile = Path.of("data.bin");
Files.write(binFile, new byte[]{1, 2, 3, 4, 5});
System.out.println("✓ write(bytes)");
// --- Reading ---
System.out.println("\n--- Reading Files ---");
// Read entire file as string (Java 11+)
String content = Files.readString(file);
System.out.println("readString(): " + content.trim());
// Read all lines
List<String> allLines = Files.readAllLines(file2);
System.out.println("readAllLines(): " + allLines);
// Read all bytes
byte[] bytes = Files.readAllBytes(binFile);
System.out.print("readAllBytes(): ");
for (byte b : bytes) System.out.print(b + " ");
System.out.println();
// --- File Properties ---
System.out.println("\n--- File Properties ---");
System.out.println("Size: " + Files.size(file) + " bytes");
System.out.println("Exists: " + Files.exists(file));
System.out.println("Is regular file: " + Files.isRegularFile(file));
System.out.println("Is directory: " + Files.isDirectory(file));
System.out.println("Is readable: " + Files.isReadable(file));
System.out.println("Is writable: " + Files.isWritable(file));
System.out.println("Last modified: " + Files.getLastModifiedTime(file));
// --- Copy and Move ---
System.out.println("\n--- Copy and Move ---");
Path copy = Path.of("demo_copy.txt");
Files.copy(file, copy, StandardCopyOption.REPLACE_EXISTING);
System.out.println("✓ Copied: " + file + " → " + copy);
Path moved = Path.of("demo_moved.txt");
Files.move(copy, moved, StandardCopyOption.REPLACE_EXISTING);
System.out.println("✓ Moved: " + copy + " → " + moved);
// --- Create Directories ---
System.out.println("\n--- Directories ---");
Path dirs = Path.of("output/reports/2024");
Files.createDirectories(dirs);
System.out.println("✓ Created: " + dirs);
// --- Cleanup ---
Files.deleteIfExists(file);
Files.deleteIfExists(file2);
Files.deleteIfExists(binFile);
Files.deleteIfExists(moved);
Files.deleteIfExists(Path.of("output/reports/2024"));
Files.deleteIfExists(Path.of("output/reports"));
Files.deleteIfExists(Path.of("output"));
System.out.println("\n✓ Cleanup complete");
}
}Walking Directory Trees
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.io.IOException;
import java.util.stream.*;
public class DirectoryWalking {
public static void main(String[] args) throws IOException {
Path startDir = Path.of(".");
System.out.println("=== Directory Walking with NIO ===\n");
// Method 1: Files.list() — non-recursive (one level)
System.out.println("--- Files.list() (one level) ---");
try (Stream<Path> entries = Files.list(startDir)) {
entries.limit(5).forEach(p ->
System.out.println(" " + (Files.isDirectory(p) ? "📁 " : "📄 ") + p.getFileName()));
}
// Method 2: Files.walk() — recursive
System.out.println("\n--- Files.walk() (recursive) ---");
try (Stream<Path> paths = Files.walk(startDir, 2)) { // max depth 2
paths.filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(".java"))
.limit(5)
.forEach(p -> System.out.println(" " + p));
}
// Method 3: Files.find() — with attributes filter
System.out.println("\n--- Files.find() (with filter) ---");
try (Stream<Path> found = Files.find(startDir, 3,
(path, attrs) -> attrs.isRegularFile() && attrs.size() > 1000)) {
found.limit(5).forEach(p -> {
try {
System.out.printf(" %s (%d bytes)%n", p.getFileName(), Files.size(p));
} catch (IOException e) { }
});
}
// Method 4: Counting and statistics
System.out.println("\n--- File Statistics ---");
try (Stream<Path> paths = Files.walk(startDir, 3)) {
var stats = paths.filter(Files::isRegularFile)
.collect(Collectors.groupingBy(
p -> getExtension(p.toString()),
Collectors.counting()));
stats.entrySet().stream()
.sorted((a, b) -> Long.compare(b.getValue(), a.getValue()))
.limit(5)
.forEach(e -> System.out.printf(" .%-6s : %d files%n", e.getKey(), e.getValue()));
}
}
static String getExtension(String filename) {
int dot = filename.lastIndexOf('.');
return dot == -1 ? "none" : filename.substring(dot + 1);
}
}Channels and Buffers
NIO introduces a channel-based I/O model using Buffers:
Output:
| ✓ Written via FileChannel | 48 bytes |
| ✓ Read via FileChannel | Hello from NIO Channels! This is efficient I/O. |
| Capacity | 20 |
| Position | 0 |
| Limit | 20 |
| Position | 12 |
| Position | 0 |
| Limit | 12 |
| Read int | 42 |
| Read double | 3.14 |
WatchService — File System Monitoring
NIO vs Traditional I/O Comparison
Common Mistakes
1. Not closing streams from Files methods
// ❌ BAD: Stream is never closed (resource leak!)
Files.walk(Path.of(".")).forEach(System.out::println);
// ✅ GOOD: Use try-with-resources
try (Stream<Path> paths = Files.walk(Path.of("."))) {
paths.forEach(System.out::println);
}2. Using readAllBytes on huge files
// ❌ BAD: Loads entire 2GB file into memory!
byte[] data = Files.readAllBytes(Path.of("huge_file.dat"));
// ✅ GOOD: Stream line by line
try (Stream<String> lines = Files.lines(Path.of("huge_file.txt"))) {
lines.filter(l -> l.contains("ERROR")).forEach(System.out::println);
}3. Forgetting to flip ByteBuffer
Interview Questions
Q1: What is the difference between java.io.File and java.nio.file.Path?
Answer: File is a legacy class (Java 1.0) representing a file path. Path (Java 7) is its modern replacement offering: better error handling (exceptions with reasons vs booleans), symbolic link support, file system independence, and integration with the Files utility class. New code should always use Path.
Q2: What are Channels and Buffers in NIO?
Answer: Channels are bidirectional connections to I/O sources (files, sockets). Buffers are containers that hold data being transferred. Data flows: Channel → Buffer (read) or Buffer → Channel (write). This is more efficient than stream-based I/O because channels can use OS-level optimizations like DMA transfers.
Q3: What is the difference between blocking and non-blocking I/O?
Answer: Blocking I/O: the thread waits (blocks) until the operation completes. Non-blocking I/O (NIO): the thread can continue doing other work — it checks if data is available without waiting. This allows one thread to manage many connections (key for scalable servers).
Q4: What is WatchService used for?
Answer: WatchService monitors file system changes (create, modify, delete events) in a directory. Use cases: hot-reloading config files, auto-compiling changed source files, sync tools (like Dropbox), log file monitoring.
Q5: What are the advantages of Files.copy() over manual stream copying?
Answer: Files.copy() uses OS-level operations (like sendfile on Linux) that can transfer data without copying through user space. It handles atomic operations, preserves file attributes, supports options like REPLACE_EXISTING and COPY_ATTRIBUTES, and provides clear exceptions on failure.
Q6: Explain ByteBuffer's flip() method.
Answer: flip() prepares a buffer for reading after writing. It sets limit = position (marks the end of written data) and position = 0 (starts reading from the beginning). Without flip, you'd try to read from the current position (end of data) and get nothing.
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Java NIO Package.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this Java Master Course topic.
Search Terms
java-master-course, java master course, java, master, course, file, handling, nio
Related Java Master Course Topics