⚡IntermediateFile I/O

Path and Files API

Navigate and manipulate the filesystem using java.nio.file.Path, Files, and directory operations.

Overview

Beyond reading and writing file content, applications often need to manage the filesystem: create directories, copy or move files, delete files, check if paths exist, and list directory contents. The java.nio.file package provides Path (an immutable path representation) and Files (a utility class with static methods for all filesystem operations). Together they replace the older, error-prone java.io.File API. Path is composable: you can resolve child paths, get parent paths, and convert between relative and absolute forms.

Key Points

  • •Path.of("dir/file.txt") creates a path; Paths.get() is the older equivalent
  • •path.getFileName(), getParent(), getRoot() decompose the path
  • •path.resolve("subdir") appends a relative path
  • •path.relativize(other) computes the relative path between two paths
  • •path.toAbsolutePath() converts to absolute path
  • •Files.exists(path), Files.isDirectory(path), Files.isRegularFile(path) check properties
  • •Files.createDirectory(path), Files.createDirectories(path) create directories
  • •Files.copy(src, dst), Files.move(src, dst) copy and move files
  • •Files.delete(path), Files.deleteIfExists(path) delete files or directories
  • •Files.list(dir) lists directory contents; Files.walk(dir) walks recursively

Examples

Path operations

import java.nio.file.*;

public class Main {
    public static void main(String[] args) {
        Path p = Path.of("projects", "myapp", "src", "Main.java");

        System.out.println("Path:      " + p);
        System.out.println("File name: " + p.getFileName());    // Main.java
        System.out.println("Parent:    " + p.getParent());      // projects/myapp/src
        System.out.println("Parts:     " + p.getNameCount());   // 4
        System.out.println("Part 0:    " + p.getName(0));       // projects
        System.out.println("Absolute:  " + p.toAbsolutePath());

        // Resolve: append a path
        Path dir = Path.of("projects/myapp");
        Path resolved = dir.resolve("src/Main.java");
        System.out.println("Resolved:  " + resolved); // projects/myapp/src/Main.java

        // Relativize: compute relative path between two paths
        Path from = Path.of("/home/user");
        Path to   = Path.of("/home/user/docs/report.txt");
        System.out.println("Relative:  " + from.relativize(to)); // docs/report.txt

        // Normalize: collapse ..
        Path ugly = Path.of("/home/user/../user/docs/../docs/file.txt");
        System.out.println("Normalized:" + ugly.normalize()); // /home/user/docs/file.txt
    }
}

Path.of() is the modern replacement for new File(). Paths are immutable, so operations like resolve() and normalize() return new Path instances.

File and directory management

import java.io.IOException;
import java.nio.file.*;

public class Main {
    public static void main(String[] args) throws IOException {
        Path tmpDir = Files.createTempDirectory("demo");

        // Create subdirectory structure
        Path subDir = tmpDir.resolve("data/reports");
        Files.createDirectories(subDir); // creates all missing parents

        System.out.println("Directory created: " + Files.isDirectory(subDir));

        // Write a file
        Path file1 = subDir.resolve("q1.txt");
        Files.writeString(file1, "Q1 report data");

        // Copy
        Path file2 = subDir.resolve("q1-backup.txt");
        Files.copy(file1, file2);
        System.out.println("Backup exists: " + Files.exists(file2));

        // Move
        Path file3 = tmpDir.resolve("archived-q1.txt");
        Files.move(file2, file3, StandardCopyOption.REPLACE_EXISTING);
        System.out.println("After move, backup exists: " + Files.exists(file2));
        System.out.println("Archive exists: " + Files.exists(file3));

        // File info
        System.out.println("File size: " + Files.size(file1) + " bytes");
        System.out.println("Last modified: " + Files.getLastModifiedTime(file1));

        // Cleanup
        Files.walk(tmpDir)
             .sorted(java.util.Comparator.reverseOrder()) // delete files before dirs
             .forEach(p -> { try { Files.delete(p); } catch (IOException e) {} });
    }
}

Files.createDirectories() creates the full path including missing parent directories. Files.copy() requires StandardCopyOption.REPLACE_EXISTING if the destination already exists.

Listing and walking directories

import java.io.IOException;
import java.nio.file.*;

public class Main {
    public static void main(String[] args) throws IOException {
        Path tmpDir = Files.createTempDirectory("tree");
        Files.createDirectories(tmpDir.resolve("src/main"));
        Files.createDirectories(tmpDir.resolve("src/test"));
        Files.writeString(tmpDir.resolve("src/main/App.java"), "// main");
        Files.writeString(tmpDir.resolve("src/test/AppTest.java"), "// test");
        Files.writeString(tmpDir.resolve("README.md"), "# readme");

        // List direct children only
        System.out.println("Direct children:");
        try (var stream = Files.list(tmpDir)) {
            stream.forEach(p -> System.out.println("  " + p.getFileName()));
        }

        // Walk entire tree (recursive)
        System.out.println("All files:");
        try (var stream = Files.walk(tmpDir)) {
            stream.filter(Files::isRegularFile)
                  .forEach(p -> System.out.println("  " + tmpDir.relativize(p)));
        }

        // Cleanup
        Files.walk(tmpDir)
             .sorted(java.util.Comparator.reverseOrder())
             .forEach(p -> { try { Files.delete(p); } catch (IOException e) {} });
    }
}

Files.list() returns a Stream<Path> of direct children. Files.walk() returns the directory itself and all descendants. Both must be closed with try-with-resources.

Common Mistakes

  • Using old java.io.File instead of java.nio.file.Path. File has poor error reporting (returns false on failure instead of throwing) and limited functionality
  • Forgetting to close Files.list() and Files.walk() streams. They hold directory handles
  • Deleting a non-empty directory with Files.delete(). This throws DirectoryNotEmptyException. Walk the tree and delete files first, then directories in reverse order

Note

To delete a directory tree recursively, use Files.walk(dir).sorted(Comparator.reverseOrder()).forEach(Files::delete). Reverse order ensures files are deleted before their parent directories.

Exercises

Write Java code and click Run to execute it instantly. When your output looks correct, mark as done.

Loading editor…

Completed: 0 / 4 exercises

Knowledge Check

Question 1 of 4

What is the difference between Files.createDirectory() and Files.createDirectories()?