Introduction to NIO.2
Java NIO.2 (New I/O 2), introduced in Java 7, revolutionized file I/O operations with the java.nio.file package. It provides enhanced file system support, improved performance, and more intuitive APIs compared to the legacy java.io.File class.
Core Components of NIO.2
1. Path Interface
The Path interface is the cornerstone of NIO.2, representing a hierarchical path in the file system.
2. Files Class
A utility class providing static methods for file operations like read, write, copy, delete, and file attribute manipulation.
3. FileSystem and FileSystems
Classes for interacting with different file system providers.
Path Interface in Depth
Creating Path Objects
import java.nio.file.Path;
import java.nio.file.Paths;
public class PathCreation {
public static void main(String[] args) {
// Different ways to create Path objects
Path path1 = Paths.get("example.txt");
Path path2 = Paths.get("/home/user/documents", "file.txt");
Path path3 = Paths.get("/", "home", "user", "documents", "file.txt");
// Using FileSystems (less common)
Path path4 = FileSystems.getDefault().getPath("example.txt");
System.out.println("Path1: " + path1);
System.out.println("Path2: " + path2);
System.out.println("Path3: " + path3);
}
}
Path Operations and Manipulation
import java.nio.file.Path;
import java.nio.file.Paths;
public class PathOperations {
public static void main(String[] args) {
Path basePath = Paths.get("/home/user/documents");
// Resolving paths
Path resolvedPath = basePath.resolve("subfolder/file.txt");
System.out.println("Resolved: " + resolvedPath);
// Getting parent directory
Path parent = resolvedPath.getParent();
System.out.println("Parent: " + parent);
// Getting file name
Path fileName = resolvedPath.getFileName();
System.out.println("File Name: " + fileName);
// Getting root component
Path root = resolvedPath.getRoot();
System.out.println("Root: " + root);
// Normalizing paths (removing redundant elements)
Path complexPath = Paths.get("/home/user/./documents/../files");
Path normalized = complexPath.normalize();
System.out.println("Normalized: " + normalized);
// Relativizing paths
Path pathA = Paths.get("/home/user");
Path pathB = Paths.get("/home/user/documents/file.txt");
Path relative = pathA.relativize(pathB);
System.out.println("Relative path: " + relative);
// Checking absolute paths
System.out.println("Is absolute: " + resolvedPath.isAbsolute());
// Converting to absolute path
Path absolute = path1.toAbsolutePath();
System.out.println("Absolute: " + absolute);
}
}
Files Class - Comprehensive File Operations
Basic File Operations
import java.nio.file.*;
import java.io.IOException;
import java.util.List;
public class BasicFileOperations {
public static void main(String[] args) {
Path filePath = Paths.get("example.txt");
Path targetPath = Paths.get("copy_example.txt");
try {
// Check if file exists
boolean exists = Files.exists(filePath);
System.out.println("File exists: " + exists);
// Check if not exists
boolean notExists = Files.notExists(filePath);
System.out.println("File not exists: " + notExists);
// Check if readable/writable/executable
boolean readable = Files.isReadable(filePath);
boolean writable = Files.isWritable(filePath);
boolean executable = Files.isExecutable(filePath);
System.out.printf("Readable: %s, Writable: %s, Executable: %s%n",
readable, writable, executable);
// Check if directory
boolean isDirectory = Files.isDirectory(filePath);
System.out.println("Is directory: " + isDirectory);
// Check if regular file
boolean isRegularFile = Files.isRegularFile(filePath);
System.out.println("Is regular file: " + isRegularFile);
} catch (Exception e) {
e.printStackTrace();
}
}
}
File Creation and Deletion
import java.nio.file.*;
import java.io.IOException;
import java.util.Arrays;
public class FileCreationDeletion {
public static void main(String[] args) {
try {
// Create a file
Path newFile = Paths.get("newfile.txt");
if (!Files.exists(newFile)) {
Files.createFile(newFile);
System.out.println("File created: " + newFile);
}
// Create directories
Path directories = Paths.get("parent/child/grandchild");
Files.createDirectories(directories); // Creates all intermediate directories
System.out.println("Directories created: " + directories);
// Create a file in the new directory
Path fileInDir = directories.resolve("nested.txt");
Files.createFile(fileInDir);
// Delete a file
Files.deleteIfExists(Paths.get("tobedeleted.txt"));
// List directory contents
Path currentDir = Paths.get(".");
System.out.println("\nDirectory contents:");
Files.list(currentDir)
.forEach(path -> System.out.println(" " + path));
} catch (IOException e) {
e.printStackTrace();
}
}
}
File Reading and Writing
import java.nio.file.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Arrays;
public class FileReadWrite {
public static void main(String[] args) {
Path filePath = Paths.get("sample.txt");
try {
// Writing to a file
List<String> lines = Arrays.asList(
"Line 1: Hello World",
"Line 2: Java NIO.2",
"Line 3: Files API Demo"
);
Files.write(filePath, lines, StandardCharsets.UTF_8);
System.out.println("File written successfully");
// Reading all lines
System.out.println("\nReading all lines:");
List<String> readLines = Files.readAllLines(filePath, StandardCharsets.UTF_8);
readLines.forEach(System.out::println);
// Reading as a single string
String content = new String(Files.readAllBytes(filePath), StandardCharsets.UTF_8);
System.out.println("\nFile content as single string:");
System.out.println(content);
// Appending to a file
Files.write(filePath,
Arrays.asList("Line 4: Appended content"),
StandardCharsets.UTF_8,
StandardOpenOption.APPEND);
System.out.println("\nAfter appending:");
Files.readAllLines(filePath, StandardCharsets.UTF_8)
.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
}
}
File Copying and Moving
import java.nio.file.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
public class FileCopyMove {
public static void main(String[] args) {
try {
// Create source file
Path source = Paths.get("source.txt");
Files.write(source,
Arrays.asList("Source file content"),
StandardCharsets.UTF_8);
// Copy file
Path copy = Paths.get("copy.txt");
Files.copy(source, copy, StandardCopyOption.REPLACE_EXISTING);
System.out.println("File copied: " + copy);
// Move/Rename file
Path moved = Paths.get("moved.txt");
Files.move(copy, moved, StandardCopyOption.REPLACE_EXISTING);
System.out.println("File moved: " + moved);
// Copy with attributes
Path copyWithAttributes = Paths.get("copy_with_attrs.txt");
Files.copy(source, copyWithAttributes,
StandardCopyOption.REPLACE_EXISTING,
StandardCopyOption.COPY_ATTRIBUTES);
// Demonstrate directory copy
Path sourceDir = Paths.get("source_dir");
Path targetDir = Paths.get("target_dir");
Files.createDirectories(sourceDir);
Files.createFile(sourceDir.resolve("file1.txt"));
Files.createFile(sourceDir.resolve("file2.txt"));
// Copy entire directory
copyDirectory(sourceDir, targetDir);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void copyDirectory(Path source, Path target) throws IOException {
Files.walk(source)
.forEach(sourcePath -> {
Path targetPath = target.resolve(source.relativize(sourcePath));
try {
if (Files.isDirectory(sourcePath)) {
Files.createDirectories(targetPath);
} else {
Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
}
Advanced File Operations
File Attributes
import java.nio.file.*;
import java.io.IOException;
import java.nio.attribute.*;
import java.util.Set;
public class FileAttributes {
public static void main(String[] args) {
Path filePath = Paths.get("attributes_demo.txt");
try {
Files.createFile(filePath);
// Basic file attributes
BasicFileAttributes basicAttrs = Files.readAttributes(filePath, BasicFileAttributes.class);
System.out.println("Creation Time: " + basicAttrs.creationTime());
System.out.println("Last Modified: " + basicAttrs.lastModifiedTime());
System.out.println("Last Access: " + basicAttrs.lastAccessTime());
System.out.println("Is Directory: " + basicAttrs.isDirectory());
System.out.println("Size: " + basicAttrs.size() + " bytes");
// POSIX file attributes (Unix/Linux)
if (FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) {
PosixFileAttributes posixAttrs = Files.readAttributes(filePath, PosixFileAttributes.class);
System.out.println("Owner: " + posixAttrs.owner().getName());
System.out.println("Group: " + posixAttrs.group().getName());
System.out.println("Permissions: " + posixAttrs.permissions());
}
// Setting file permissions
Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-r--r--");
Files.setPosixFilePermissions(filePath, perms);
// DOS file attributes (Windows)
if (FileSystems.getDefault().supportedFileAttributeViews().contains("dos")) {
DosFileAttributes dosAttrs = Files.readAttributes(filePath, DosFileAttributes.class);
System.out.println("Is Hidden: " + dosAttrs.isHidden());
System.out.println("Is Archive: " + dosAttrs.isArchive());
System.out.println("Is Read-only: " + dosAttrs.isReadOnly());
System.out.println("Is System: " + dosAttrs.isSystem());
}
// Setting last modified time
FileTime newTime = FileTime.fromMillis(System.currentTimeMillis());
Files.setLastModifiedTime(filePath, newTime);
} catch (IOException e) {
e.printStackTrace();
} catch (UnsupportedOperationException e) {
System.out.println("Operation not supported on this platform: " + e.getMessage());
}
}
}
Directory Walking and File Searching
import java.nio.file.*;
import java.io.IOException;
import java.util.stream.Stream;
public class DirectoryWalking {
public static void main(String[] args) {
Path startDir = Paths.get(".");
try {
// Walk through directory tree
System.out.println("Walking directory tree:");
Files.walk(startDir)
.filter(Files::isRegularFile)
.forEach(path -> {
try {
System.out.println("File: " + path +
" Size: " + Files.size(path) + " bytes");
} catch (IOException e) {
System.err.println("Error reading file: " + path);
}
});
// Find files using pattern matching
System.out.println("\nFinding Java files:");
PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:**.java");
Files.find(startDir,
Integer.MAX_VALUE,
(path, attrs) -> matcher.matches(path) && attrs.isRegularFile())
.forEach(System.out::println);
// List directory contents with DirectoryStream
System.out.println("\nDirectory contents (max depth 2):");
try (Stream<Path> paths = Files.walk(startDir, 2)) {
paths.forEach(path -> {
String type = Files.isDirectory(path) ? "DIR " : "FILE";
System.out.println(type + " " + path);
});
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Temporary Files and Directories
import java.nio.file.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class TemporaryFiles {
public static void main(String[] args) {
try {
// Create temporary file
Path tempFile = Files.createTempFile("prefix_", "_suffix.txt");
System.out.println("Temporary file: " + tempFile);
// Write to temporary file
Files.write(tempFile,
"Temporary file content".getBytes(StandardCharsets.UTF_8));
// Create temporary directory
Path tempDir = Files.createTempDirectory("temp_dir_");
System.out.println("Temporary directory: " + tempDir);
// Create file in temporary directory
Path fileInTempDir = tempDir.resolve("temp_file.txt");
Files.createFile(fileInTempDir);
// List temporary directory contents
System.out.println("Temporary directory contents:");
Files.list(tempDir).forEach(System.out::println);
// Note: Temporary files are not automatically deleted
// You need to manually delete them or use deleteOnExit()
tempFile.toFile().deleteOnExit();
fileInTempDir.toFile().deleteOnExit();
tempDir.toFile().deleteOnExit();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Practical Examples and Use Cases
File Monitoring with WatchService
import java.nio.file.*;
import java.io.IOException;
import static java.nio.file.StandardWatchEventKinds.*;
public class FileWatcher {
public static void main(String[] args) {
Path watchDir = Paths.get(".");
try {
WatchService watchService = FileSystems.getDefault().newWatchService();
// Register for events
watchDir.register(watchService,
ENTRY_CREATE,
ENTRY_DELETE,
ENTRY_MODIFY);
System.out.println("Watching directory: " + watchDir.toAbsolutePath());
System.out.println("Press Ctrl+C to stop...");
while (true) {
WatchKey key = watchService.take(); // Blocks until event occurs
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
Path fileName = (Path) event.context();
System.out.println(kind + ": " + fileName);
if (kind == ENTRY_CREATE) {
System.out.println(" New file created: " + fileName);
} else if (kind == ENTRY_MODIFY) {
System.out.println(" File modified: " + fileName);
} else if (kind == ENTRY_DELETE) {
System.out.println(" File deleted: " + fileName);
}
}
boolean valid = key.reset();
if (!valid) {
break;
}
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
File Utility Class
import java.nio.file.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.stream.Collectors;
public class FileUtils {
// Read file content as string
public static String readFile(Path filePath) throws IOException {
return new String(Files.readAllBytes(filePath), StandardCharsets.UTF_8);
}
// Write string to file
public static void writeFile(Path filePath, String content) throws IOException {
Files.write(filePath, content.getBytes(StandardCharsets.UTF_8));
}
// Find files by extension
public static List<Path> findFilesByExtension(Path startDir, String extension)
throws IOException {
String pattern = "glob:**." + extension;
PathMatcher matcher = FileSystems.getDefault().getPathMatcher(pattern);
return Files.walk(startDir)
.filter(path -> matcher.matches(path) && Files.isRegularFile(path))
.collect(Collectors.toList());
}
// Copy directory recursively
public static void copyDirectoryRecursive(Path source, Path target) throws IOException {
Files.walk(source)
.forEach(sourcePath -> {
Path targetPath = target.resolve(source.relativize(sourcePath));
try {
if (Files.isDirectory(sourcePath)) {
Files.createDirectories(targetPath);
} else {
Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
// Get file information
public static void printFileInfo(Path filePath) throws IOException {
BasicFileAttributes attrs = Files.readAttributes(filePath, BasicFileAttributes.class);
System.out.println("File: " + filePath);
System.out.println("Size: " + attrs.size() + " bytes");
System.out.println("Created: " + attrs.creationTime());
System.out.println("Modified: " + attrs.lastModifiedTime());
System.out.println("Is Directory: " + attrs.isDirectory());
System.out.println("Is Regular File: " + attrs.isRegularFile());
}
public static void main(String[] args) {
try {
// Demonstrate utility methods
Path testFile = Paths.get("test_utility.txt");
writeFile(testFile, "Utility class demonstration");
String content = readFile(testFile);
System.out.println("File content: " + content);
printFileInfo(testFile);
// Clean up
Files.deleteIfExists(testFile);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Error Handling and Best Practices
Proper Exception Handling
import java.nio.file.*;
import java.io.IOException;
public class ErrorHandling {
public static void main(String[] args) {
Path filePath = Paths.get("nonexistent.txt");
try {
// This will throw NoSuchFileException
Files.readAllLines(filePath);
} catch (NoSuchFileException e) {
System.err.println("File does not exist: " + e.getFile());
} catch (AccessDeniedException e) {
System.err.println("Access denied: " + e.getFile());
} catch (IOException e) {
System.err.println("General I/O error: " + e.getMessage());
} finally {
System.out.println("Cleanup operations can go here");
}
}
}
Performance Considerations
1. Use Buffered Operations for Large Files
import java.nio.file.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
public class BufferedOperations {
// Efficient for large files
public static void processLargeFile(Path inputPath, Path outputPath) throws IOException {
try (BufferedReader reader = Files.newBufferedReader(inputPath, StandardCharsets.UTF_8);
BufferedWriter writer = Files.newBufferedWriter(outputPath, StandardCharsets.UTF_8)) {
String line;
while ((line = reader.readLine()) != null) {
// Process line
String processedLine = line.toUpperCase();
writer.write(processedLine);
writer.newLine();
}
}
}
// Using streams for binary data
public static void copyLargeFile(Path source, Path target) throws IOException {
try (InputStream in = Files.newInputStream(source);
OutputStream out = Files.newOutputStream(target)) {
byte[] buffer = new byte[8192]; // 8KB buffer
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
}
}
}
Conclusion
The NIO.2 Path and Files API provides a robust, modern approach to file system operations in Java. Key advantages include:
- Type Safety: Strongly typed APIs reduce runtime errors
- Performance: Better performance than legacy I/O
- Flexibility: Support for multiple file systems and custom providers
- Comprehensive Features: File watching, attributes, symbolic links
- Stream Integration: Seamless integration with Java Streams API
When working with NIO.2:
- Always handle exceptions appropriately
- Use try-with-resources for automatic resource management
- Consider performance implications for large files
- Leverage the rich set of file attributes and metadata
- Utilize the WatchService for real-time file monitoring
This powerful API makes file operations in Java more intuitive, efficient, and maintainable for modern applications.