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.