šŸ“ File I/O and NIO.2

Master modern file operations through NIO.2 architecture, path manipulation mastery, and advanced I/O patterns for robust file handling


šŸŽÆ Learning Objectives

By the end of this lesson, you will:


šŸ—ļø File I/O Evolution Architecture

<aside> šŸš€

NIO.2 Philosophy

NIO.2 (New I/O 2) treats the file system as a navigable data structure rather than just strings. Paths are like GPS coordinates for files, providing precise navigation and rich metadata access through a modern, exception-safe API.

</aside>

Traditional I/O vs NIO.2 Comparison

File I/O Evolution - From Legacy to Modern
═══════════════════════════════════════════════════════════════════════════════

    šŸ“œ LEGACY [java.io](<http://java.io>).File              šŸš€ MODERN NIO.2 (java.nio.file)
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”   ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│ File file = new File("path.txt");  │   │ Path path = Paths.get("path.txt");  │
│                                     │   │                                     │
│ āŒ PROBLEMS:                        │   │ āœ… BENEFITS:                        │
│ • String-based paths               │   │ • Structured Path objects           │
│ • Limited metadata access         │   │ • Rich attribute support           │
│ • Platform-specific behavior      │   │ • Platform abstraction            │
│ • Poor error handling             │   │ • Detailed exceptions              │
│ • No symbolic link support        │   │ • Full link support                │
│ • Blocking operations only        │   │ • Async and watch capabilities     │
│                                     │   │                                     │
│ if (file.exists()) {               │   │ if (Files.exists(path)) {          │
│   file.delete(); // boolean        │   │   Files.delete(path); // throws    │
│ }                                  │   │ }                                   │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜   ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

    šŸ›¤ļø PATH STRUCTURE VISUALIZATION
    ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
    │ /home/user/projects/java-app/src/main/java/[App.java](<http://App.java>)                    │
    │  ā””ā”€ā”¬ā”€ā”˜ ā””ā”€ā”¬ā”€ā”˜ ā””ā”€ā”€ā”¬ā”€ā”€ā”€ā”˜ ā””ā”€ā”€ā”¬ā”€ā”€ā”€ā”˜ ā””ā”¬ā”˜ ā””ā”€ā”¬ā”€ā”˜ ā””ā”€ā”¬ā”€ā”˜ ā””ā”€ā”¬ā”€ā”˜                   │
    │   root  user  projects java-app src main java filename                │
    │                                                                         │
    │ Path Methods:                                                           │
    │ • getRoot()        → /                                                 │
    │ • getParent()      → /home/user/projects/java-app/src/main/java       │
    │ • getFileName()    → [App.java](<http://App.java>)                                         │
    │ • getNameCount()   → 7                                                │
    │ • getName(2)       → projects                                         │
    ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

šŸ›¤ļø Path Interface Mastery

Path Creation and Navigation

<aside> 🧭

Path Navigation Philosophy

Think of Paths as precise coordinates in the file system. They can be absolute (full address) or relative (directions from current location), and can be manipulated like mathematical operations.

</aside>

šŸ“‚ Project File Navigator

public class ProjectFileNavigator {
    
    // šŸ“‚ Project structure constants
    public static final String PROJECT_ROOT = "/home/user/projects/java-app";
    public static final String SOURCE_DIR = "src/main/java";
    public static final String RESOURCE_DIR = "src/main/resources";
    public static final String TEST_DIR = "src/test/java";
    
    private Path projectRoot;
    private Path sourceDir;
    private Path resourceDir;
    private Path testDir;
    
    public ProjectFileNavigator(String projectPath) throws IOException {
        initializePaths(projectPath);
        validateProjectStructure();
    }
    
    private void initializePaths(String projectPath) {
        // šŸ—ļø Creating paths in different ways
        projectRoot = Paths.get(projectPath);
        
        // Absolute path creation
        Path absoluteSource = Paths.get(projectPath, SOURCE_DIR);
        
        // Relative path creation
        Path relativeSource = Paths.get(SOURCE_DIR);
        
        // URI-based path creation
        try {
            Path uriPath = Paths.get(new URI("[file://](file://)" + projectPath));
            System.out.println("URI path: " + uriPath);
        } catch (URISyntaxException e) {
            System.err.println("Invalid URI syntax: " + e.getMessage());
        }
        
        // Path resolution (joining paths)
        sourceDir = projectRoot.resolve(SOURCE_DIR);
        resourceDir = projectRoot.resolve(RESOURCE_DIR);
        testDir = projectRoot.resolve(TEST_DIR);
        
        System.out.println("šŸ—ļø Project Paths Initialized:");
        System.out.println("  Root: " + projectRoot);
        System.out.println("  Source: " + sourceDir);
        System.out.println("  Resources: " + resourceDir);
        System.out.println("  Tests: " + testDir);
    }
    
    // šŸ” Path analysis and manipulation
    public void demonstratePathOperations() {
        System.out.println("\\nšŸ” Path Analysis Operations");
        System.out.println("===========================");
        
        Path sampleFile = sourceDir.resolve("com/example/[App.java](<http://App.java>)");
        
        // Basic path information
        System.out.println("Sample file: " + sampleFile);
        System.out.println("  Is absolute: " + sampleFile.isAbsolute());
        System.out.println("  Root: " + sampleFile.getRoot());
        System.out.println("  Parent: " + sampleFile.getParent());
        System.out.println("  File name: " + sampleFile.getFileName());
        System.out.println("  Name count: " + sampleFile.getNameCount());
        
        // Path components
        System.out.println("\\nPath components:");
        for (int i = 0; i < sampleFile.getNameCount(); i++) {
            System.out.println("  [" + i + "] " + sampleFile.getName(i));
        }
        
        // Path manipulation
        Path normalized = sampleFile.normalize();
        Path parent = sampleFile.getParent();
        Path resolved = parent.resolve("../other/[File.java](<http://File.java>)");
        Path normalizedResolved = resolved.normalize();
        
        System.out.println("\\nPath manipulation:");
        System.out.println("  Original: " + sampleFile);
        System.out.println("  Normalized: " + normalized);
        System.out.println("  Resolved: " + resolved);
        System.out.println("  Resolved normalized: " + normalizedResolved);
        
        // Path relationships
        demonstratePathRelationships();
        
        // Path comparison
        demonstratePathComparison();
    }
    
    private void demonstratePathRelationships() {
        System.out.println("\\nšŸ”— Path Relationships:");
        
        Path javaFile = sourceDir.resolve("[App.java](<http://App.java>)");
        Path parentDir = sourceDir;
        Path siblingFile = sourceDir.resolve("[Config.java](<http://Config.java>)");
        
        // Parent-child relationships
        System.out.println("Java file starts with source dir: " + 
                          javaFile.startsWith(parentDir));
        System.out.println("Java file ends with '[App.java](<http://App.java>)': " + 
                          javaFile.endsWith("[App.java](<http://App.java>)"));
        
        // Sibling relationships
        Path sibling = javaFile.resolveSibling("[Config.java](<http://Config.java>)");
        System.out.println("Sibling of [App.java](<http://App.java>): " + sibling);
        
        // Relative paths
        try {
            Path relativePath = parentDir.relativize(javaFile);
            System.out.println("Relative path from source to file: " + relativePath);
            
            Path reconstructed = parentDir.resolve(relativePath);
            System.out.println("Reconstructed path: " + reconstructed);
            System.out.println("Paths equal: " + javaFile.equals(reconstructed));
        } catch (IllegalArgumentException e) {
            System.out.println("Cannot relativize paths: " + e.getMessage());
        }
    }
    
    private void demonstratePathComparison() {
        System.out.println("\\nāš–ļø Path Comparison:");
        
        Path path1 = Paths.get("/home/user/documents/file.txt");
        Path path2 = Paths.get("/home/user/../user/documents/file.txt");
        Path path3 = Paths.get("documents/file.txt");
        
        // Lexical comparison
        System.out.println("Path1: " + path1);
        System.out.println("Path2: " + path2);
        System.out.println("Lexical equality (path1 == path2): " + path1.equals(path2));
        System.out.println("After normalization: " + path1.equals(path2.normalize()));
        
        // File system comparison (requires existing files)
        try {
            // This would compare actual file system entities
            // boolean sameFile = Files.isSameFile(path1, path2);
            // System.out.println("Same file in filesystem: " + sameFile);
        } catch (Exception e) {
            System.out.println("Cannot compare files (may not exist): " + e.getMessage());
        }
        
        // Path ordering
        List<Path> paths = Arrays.asList(
            Paths.get("zebra.txt"),
            Paths.get("apple.txt"),
            Paths.get("banana.txt")
        );
        
        System.out.println("\\nPath sorting:");
        [paths.stream](<http://paths.stream>)()
             .sorted()
             .forEach(path -> System.out.println("  " + path));
    }
    
    // šŸ“ Directory structure validation
    private void validateProjectStructure() throws IOException {
        System.out.println("\\nšŸ“ Project Structure Validation");
        System.out.println("===============================");
        
        validateDirectory(projectRoot, "Project Root");
        validateDirectory(sourceDir, "Source Directory");
        validateDirectory(resourceDir, "Resource Directory");
        validateDirectory(testDir, "Test Directory");
    }
    
    private void validateDirectory(Path dir, String description) throws IOException {
        if (Files.exists(dir)) {
            if (Files.isDirectory(dir)) {
                System.out.println("āœ… " + description + " exists: " + dir);
                
                // Show directory contents
                try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
                    System.out.println("    Contents:");
                    for (Path entry : stream) {
                        String type = Files.isDirectory(entry) ? "šŸ“" : "šŸ“„";
                        System.out.println("      " + type + " " + entry.getFileName());
                    }
                } catch (IOException e) {
                    System.out.println("    āŒ Cannot read directory contents: " + e.getMessage());
                }
            } else {
                System.out.println("āš ļø " + description + " exists but is not a directory: " + dir);
            }
        } else {
            System.out.println("āŒ " + description + " does not exist: " + dir);
            
            // Create directory structure if needed
            System.out.println("    Creating directory structure...");
            Files.createDirectories(dir);
            System.out.println("    āœ… Directory created: " + dir);
        }
    }
    
    // šŸŽÆ Advanced path operations
    public void demonstrateAdvancedOperations() {
        System.out.println("\\nšŸš€ Advanced Path Operations");
        System.out.println("===========================");
        
        // Working with different file systems
        demonstrateFileSystemOperations();
        
        // Path conversion operations
        demonstratePathConversions();
        
        // Complex path manipulations
        demonstrateComplexManipulations();
    }
    
    private void demonstrateFileSystemOperations() {
        System.out.println("šŸ–„ļø File System Operations:");
        
        // Get default file system
        FileSystem defaultFS = FileSystems.getDefault();
        System.out.println("Default file system: " + defaultFS);
        System.out.println("Separator: '" + defaultFS.getSeparator() + "'");
        System.out.println("Root directories:");
        for (Path root : defaultFS.getRootDirectories()) {
            System.out.println("  " + root);
        }
        
        // File system attributes
        System.out.println("File stores:");
        for (FileStore store : defaultFS.getFileStores()) {
            try {
                System.out.println("  " + [store.name](<http://store.name>)() + " (" + store.type() + ")");
                System.out.println("    Total: " + formatBytes(store.getTotalSpace()));
                System.out.println("    Available: " + formatBytes(store.getUsableSpace()));
            } catch (IOException e) {
                System.out.println("    Cannot read store info: " + e.getMessage());
            }
        }
    }
    
    private void demonstratePathConversions() {
        System.out.println("\\nšŸ”„ Path Conversions:");
        
        Path path = sourceDir.resolve("[App.java](<http://App.java>)");
        
        // Convert to File (legacy compatibility)
        File file = path.toFile();
        System.out.println("Path to File: " + file);
        
        // Convert back to Path
        Path pathFromFile = file.toPath();
        System.out.println("File to Path: " + pathFromFile);
        System.out.println("Round-trip equality: " + path.equals(pathFromFile));
        
        // Convert to URI
        URI uri = path.toUri();
        System.out.println("Path to URI: " + uri);
        
        // Convert to absolute path
        Path absolute = path.toAbsolutePath();
        System.out.println("Absolute path: " + absolute);
        
        // Convert to real path (resolves symbolic links)
        try {
            Path realPath = path.toRealPath();
            System.out.println("Real path: " + realPath);
        } catch (IOException e) {
            System.out.println("Cannot resolve real path: " + e.getMessage());
        }
    }
    
    private void demonstrateComplexManipulations() {
        System.out.println("\\n🧩 Complex Path Manipulations:");
        
        // Build complex path hierarchies
        Path baseConfig = resourceDir.resolve("config");
        Path envConfig = baseConfig.resolve("environments");
        Path devConfig = envConfig.resolve("[dev.properties](<http://dev.properties>)");
        Path prodConfig = envConfig.resolve("[prod.properties](<http://prod.properties>)");
        
        System.out.println("Configuration hierarchy:");
        System.out.println("  Base: " + baseConfig);
        System.out.println("  Environment: " + envConfig);
        System.out.println("  Development: " + devConfig);
        System.out.println("  Production: " + prodConfig);
        
        // Find common parent
        Path commonParent = findCommonParent(devConfig, prodConfig);
        System.out.println("Common parent: " + commonParent);
        
        // Create relative paths between configurations
        try {
            Path devToProd = devConfig.getParent().relativize(prodConfig);
            System.out.println("Dev to Prod relative: " + devToProd);
        } catch (IllegalArgumentException e) {
            System.out.println("Cannot create relative path: " + e.getMessage());
        }
    }
    
    // šŸ”§ Utility methods
    private Path findCommonParent(Path path1, Path path2) {
        Path p1 = path1.toAbsolutePath().normalize();
        Path p2 = path2.toAbsolutePath().normalize();
        
        while (p1 != null && !p2.startsWith(p1)) {
            p1 = p1.getParent();
        }
        
        return p1;
    }
    
    private String formatBytes(long bytes) {
        if (bytes < 1024) return bytes + " B";
        if (bytes < 1024 * 1024) return String.format("%.1f KB", bytes / 1024.0);
        if (bytes < 1024 * 1024 * 1024) return String.format("%.1f MB", bytes / (1024.0 * 1024));
        return String.format("%.1f GB", bytes / (1024.0 * 1024 * 1024));
    }
    
    // šŸ“Š Path statistics
    public void generatePathStatistics() throws IOException {
        System.out.println("\\nšŸ“Š Path Statistics");
        System.out.println("==================");
        
        System.out.println("Project structure analysis:");
        analyzeDirectory(projectRoot, 0, 3);
    }
    
    private void analyzeDirectory(Path dir, int level, int maxLevel) throws IOException {
        if (level > maxLevel || !Files.exists(dir) || !Files.isDirectory(dir)) {
            return;
        }
        
        String indent = "  ".repeat(level);
        System.out.println(indent + "šŸ“ " + dir.getFileName() + "/");
        
        try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
            for (Path entry : stream) {
                if (Files.isDirectory(entry)) {
                    analyzeDirectory(entry, level + 1, maxLevel);
                } else {
                    try {
                        long size = Files.size(entry);
                        System.out.println(indent + "  šŸ“„ " + entry.getFileName() + 
                                         " (" + formatBytes(size) + ")");
                    } catch (IOException e) {
                        System.out.println(indent + "  šŸ“„ " + entry.getFileName() + " (size unknown)");
                    }
                }
            }
        }
    }
}