Study guide
11.2 Java NIO.2 File API: Path, Paths, and File System Operations
Oracle Java SE 21 Developer 1Z0-830
Track your progress as you read
Finished sections get a check mark.
1 Chapter 1: Java Basics, Primitive Types, and Operators
1.1 Program Structure, Primitives, Literals, Scopes, and var 1.2 Operators, Precedence, and Type Casting 1.3 Strings, Text Blocks, and StringBuilder 1.4 Date and Time APIs (java.time)
2 Chapter 2: Controlling Program Flow & Pattern Matching
2.1 Conditional Logic: if-else and Ternary Operators 2.2 Switch Statements and Switch Expressions 2.3 Pattern Matching for switch and instanceof 2.4 Loops and Branching Statements
3 Chapter 3: Object-Oriented Design & Class Architecture
3.1 Class Declarations, Constructors, and Object Lifecycle 3.2 Encapsulation and Access Modifiers 3.3 Inheritance, Method Overriding, and Polymorphism 3.4 Abstract Classes and Interfaces 3.5 Nested, Inner, Local, and Anonymous Classes 3.6 Method Overloading, Overload Resolution, and Varargs
4 Chapter 4: Modern Java Data Modeling: Records, Enums, and Sealed Types
4.1 Java Records, Canonical/Compact Constructors, and Component Accessors 4.2 Java Enums: Fields, Methods, and Abstract Constant-Specific Behavior 4.3 Sealed Classes and Interfaces: permits, final, non-sealed, and Hierarchy Architecture 4.4 Advanced Record Patterns (JEP 440), Nested Deconstruction, and Sealed Hierarchy Exhaustiveness
5 Chapter 5: Exception Handling and Annotations
5.1 Exception Hierarchy: Checked vs. Unchecked Exceptions 5.2 Exception Handling: try, catch, finally, and Multi-Catch 5.3 Resource Management: try-with-resources and AutoCloseable 5.4 Custom Exceptions and Best Practices 5.5 Java Annotations: Built-in, Meta-Annotations, and Custom Types
6 Chapter 6: Generics and the Collections Framework
6.1 Generics Fundamentals and Generic Methods 6.2 Wildcards, Type Bounds, and Type Erasure 6.3 Core Collections: Lists, Sets, Queues, and Maps 6.4 Sequenced Collections in Java 21 6.5 Sorting, Comparable, and Comparator 6.6 Arrays: Creation, Multidimensional Forms, and the Arrays Utility Class
7 Chapter 7: Functional Programming & Lambda Expressions
7.1 Built-in Functional Interfaces in java.util.function 7.2 Lambda Expressions, Variable Scopes, and Effectively Final 7.3 Method References: Four Varieties and Edge Cases 7.4 Working with java.util.Optional
8 Chapter 8: The Stream API and Data Pipelines
8.1 Stream Creation and Intermediate Operations 8.2 Terminal Operations and Reductions 8.3 Advanced Collectors, Grouping, Partitioning, and Teeing 8.4 Primitive Streams and Parallel Stream Processing
9 Chapter 9: Concurrency, Multithreading, and Virtual Threads
9.1 Thread Fundamentals, Creation, and Lifecycle 9.2 Thread Safety, Synchronization, and the Java Memory Model 9.3 Concurrency Utilities: ExecutorService, Callable, and Future 9.4 Concurrent Collections, Atomics, and Explicit Locks 9.5 Virtual Threads in Java 21 (Carrier Threads, Pinning, and Best Practices)
10 Chapter 10: The Java Platform Module System (JPMS)
10.1 Module Architecture and module-info.java Declarations 10.2 Module Directives: requires, exports, opens, provides, and uses 10.3 Modular Compilation, Packaging, and CLI Execution 10.4 Module Types, Migration Strategies, ServiceLoader SPI, and JLink
11 Chapter 11: Java I/O, NIO.2, and Object Serialization
11.1 Classic Java I/O Streams and Readers/Writers 11.2 Java NIO.2 File API: Path, Paths, and File System Operations 11.3 Advanced NIO.2 Stream Operations and Directory Traversal 11.4 Java Object Serialization and Record Serialization
12 Chapter 12: Implementing Localization and Internationalization
12.1 Locales and Resource Bundles 12.2 Formatting and Parsing Numbers, Currencies, Dates, Times, and Messages
11.2 Java NIO.2 File API: Path, Paths, and File System Operations
Key Takeaways Explain This Topic AI
Path represents an immutable, hierarchical file system location instantiated via Path.of(...) in modern Java, operating purely on lexical representations in memory without requiring disk access.
Lexical path methods such as normalize(), relativize(), resolve(), and subpath() manipulate path structures syntactically, while toRealPath() accesses the physical file system to verify existence and resolve symlinks.
The relativize() method requires both paths to be either absolute or relative, throwing IllegalArgumentException if types are mixed or if paths on Windows reside on different root drives.
The subpath(begin, end) method uses 0-based element indexing, excludes the root component, and always constructs a relative Path.
The Files utility class provides atomic and attribute-aware operations (copy, move, deleteIfExists, readString, writeString) where directory copy is strictly shallow by default.
Last updated: September 2026
Java NIO.2 File API: Path, Paths, and File System Operations
Java 7 introduced NIO.2 (New I/O 2) under the java.nio.file package, completely modernizing file system interactions and overcoming severe architectural limitations of the legacy java.io.File class. NIO.2 introduces rich, type-safe abstractions for path manipulation, cross-platform file attribute inspection, symbolic link resolution, atomic operations, and extensible file system providers (including ZIP file systems and in-memory virtual file systems).
For the Oracle Certified Professional: Java SE 21 Developer (1Z0-830) exam, developers must master the immutable java.nio.file.Path interface, distinguish strictly between in-memory lexical operations and physical disk-based operations , and understand the precise behavior of static helper methods in java.nio.file.Files .
1. The Path Interface and Factory Methods
A Path represents a hierarchical, platform-specific location of a file or directory within a file system. Path objects are immutable , comparable , and safe to share across concurrent execution threads.
Creating Path Instances
Java SE 21 supports several approaches to instantiate Path objects: // 1. Recommended Modern Approach (Java 11+): Path.of()
- Path p1 = Path.of("data", "reports", "annual.csv");
- Path p2 = Path.of("/var/log/application.log");
- Path p3 = Path.of(URI.create("file:///home/developer/config.json"));
- // 2. Classic NIO.2 Factory (Java 7+): Paths.get()
- Path p4 = Paths.get("data", "reports", "annual.csv");
- Path p5 = Paths.get(URI.create("file:///opt/app/server.xml"));
// 3. FileSystems Factory:
Path p6 = FileSystems.getDefault().getPath("data", "reports", "annual.csv");
// 4. Interoperability with Legacy java.io.File:
- File legacyFile = p1.toFile();
- Path modernPath = legacyFile.toPath();
Note
Path.of(...) (introduced in Java 11) is a direct syntactic shortcut for Paths.get(...) . Both methods delegate internally to FileSystems.getDefault().getPath(...) . On modern Java exams, Path.of(...) is the preferred idiom.
2. Lexical Path Inspection and Slicing (In-Memory Only)
Lexical path operations inspect and manipulate the path's text structure in JVM memory without interacting with the physical operating system file system. The targeted files or directories do not need to exist on disk for lexical operations to execute successfully. Path path = Path.of("/projects/2026/finance/summary.txt");
- System.out.println(path.toString()); // "/projects/2026/finance/summary.txt"
- System.out.println(path.getFileName()); // "summary.txt" (returned as a Path instance)
- System.out.println(path.getParent()); // "/projects/2026/finance" (Path instance)
System.out.println(path.getRoot()); // "/" on Unix; "C:\" on Windows; null if relative!
System.out.println(path.isAbsolute()); // true
// Name element indexing: 0-indexed, EXCLUDES ROOT!
- System.out.println(path.getNameCount()); // 4 (projects, 2026, finance, summary.txt)
- System.out.println(path.getName(0)); // "projects" (Path instance)
- System.out.println(path.getName(2)); // "finance" (Path instance)
- The subpath(int beginIndex, int endIndex) Method
The subpath() method slices a portion of a path's name element sequence:
Indexing : Uses 0-based indexing over the path's name elements.
Parameters : beginIndex is inclusive; endIndex is exclusive .
Relative Path Rule : subpath() never includes the root component ( / or C:\ ). The resulting Path is always a relative path, even if sliced from an absolute path!
Exception Rule : Throws java.lang.IllegalArgumentException at runtime if:
- beginIndex < 0
- beginIndex >= path.getNameCount()
- endIndex <= beginIndex
- endIndex > path.getNameCount() Path p = Path.of("/home/developer/workspace/app/Main.java");
- // Name elements: [0] home, [1] developer, [2] workspace, [3] app, [4] Main.java
- // Total getNameCount() == 5
- Path sub1 = p.subpath(1, 4);
- System.out.println(sub1); // "developer/workspace/app" (Relative path!)
- System.out.println(sub1.isAbsolute()); // false
// Slicing single element:
- Path sub2 = p.subpath(0, 1);
- System.out.println(sub2); // "home" (Relative path, no leading slash!)
- Element Matching: startsWith() and endsWith()
The startsWith(Path/String) and endsWith(Path/String) methods in NIO.2 match complete path name elements , not arbitrary string character prefixes/suffixes: Path path = Path.of("/data/reports/2026.csv");
// Matching complete name elements:
- System.out.println(path.startsWith("/data")); // true
- System.out.println(path.startsWith("/data/reports")); // true
- System.out.println(path.endsWith("2026.csv")); // true
- System.out.println(path.endsWith(Path.of("reports/2026.csv"))); // true
// TRAP: Substring matching fails!
- System.out.println(path.startsWith("/dat")); // false (not a complete element)
- System.out.println(path.endsWith(".csv")); // false (not a complete element)
- 3. Lexical Transformations: normalize() , resolve() , and relativize()
- normalize()
Eliminates redundant name elements such as . (current directory) and .. (parent directory navigation) by analyzing the string hierarchy: Path dirty = Path.of("/var/log/../log/./app/debug.log");
- Path clean = dirty.normalize();
- System.out.println(clean); // "/var/log/app/debug.log"
- Path relativeDirty = Path.of("a/b/../../c");
- System.out.println(relativeDirty.normalize()); // "c"
- resolve() and resolveSibling()
The resolve() method joins two paths together (hierarchical path concatenation):
path.resolve(Path other) / path.resolve(String other) :
If other is a relative path , resolve() appends other to path .
If other is an absolute path , resolve() discards path and returns other unchanged !
If other is empty ( Path.of("") ), resolve() returns path .
path.resolveSibling(Path other) :
Resolves other against path.getParent() , effectively replacing the file name element. Path base = Path.of("/opt/app");
- Path rel = Path.of("config/server.xml");
- Path abs = Path.of("/etc/hosts");
- System.out.println(base.resolve(rel)); // "/opt/app/config/server.xml"
- System.out.println(base.resolve(abs)); // "/etc/hosts" (Absolute argument wins!)
- Path file = Path.of("/opt/app/application.log");
- System.out.println(file.resolveSibling("config.json")); // "/opt/app/config.json"
- relativize() (Navigational Step Construction)
The relativize() method calculates the relative navigational path required to travel from this path to other path: Path p1 = Path.of("/usr/local/bin");
Path p2 = Path.of("/usr/share/doc");
- Path relative = p1.relativize(p2);
- System.out.println(relative); // "../../share/doc"
- Golden Exam Traps for relativize()
Warning
Mixed Path Types : Attempting to relativize between an absolute path and a relative path throws java.lang.IllegalArgumentException at runtime! Both paths must be absolute, or both paths must be relative.
Different Roots (Windows) : On Windows file systems, attempting to relativize across different drive root letters (e.g., C:\projects and D:\data ) throws java.lang.IllegalArgumentException because no relative path can navigate across independent root partitions. Path absPath = Path.of("/usr/bin");
Path relPath = Path.of("data/file.txt");
// RUNTIME ERROR: Throws IllegalArgumentException!
- absPath.relativize(relPath);
- 4. Physical Path Resolution: toAbsolutePath() vs. toRealPath()
NIO.2 provides two distinct methods to convert paths to absolute forms:
- Feature toAbsolutePath() toRealPath(LinkOption... options)
- File System Access Lexical only (No disk I/O) Physical (Queries operating system)
- Disk Existence Required? No (File does not need to exist) Yes (Throws NoSuchFileException if missing)
- Symbolic Link Resolution Keeps symbolic links unresolved Resolves symbolic links to target files
- Normalization Retains . and .. components Automatically normalizes redundant components
Checked Exceptions None ( toAbsolutePath() never throws) Throws IOException ( NoSuchFileException ) Path relPath = Path.of("config.json");
- // Purely lexical: prepends current working directory
- Path abs = relPath.toAbsolutePath();
- System.out.println(abs); // "/home/developer/workspace/config.json" (No disk verification!)
// Physical verification:
- try {
- Path real = relPath.toRealPath(LinkOption.NOFOLLOW_LINKS);
- System.out.println("Canonical physical path: " + real);
- } catch (NoSuchFileException e) {
- System.err.println("File does not exist on disk: " + e.getFile());
- }
- 5. File Operations with the java.nio.file.Files Utility Class
The Files class contains static helper methods that operate directly on the underlying file system.
Verification and Attribute Inspection Path p = Path.of("/data/report.pdf");
// Existence checks:
- boolean exists = Files.exists(p, LinkOption.NOFOLLOW_LINKS);
- boolean notExists = Files.notExists(p);
// File type queries:
- boolean isFile = Files.isRegularFile(p);
- boolean isDir = Files.isDirectory(p);
- boolean isSym = Files.isSymbolicLink(p);
- boolean isReadable = Files.isReadable(p);
- boolean isWritable = Files.isWritable(p);
- boolean isExecutable = Files.isExecutable(p);
- boolean isHidden = Files.isHidden(p);
- long sizeInBytes = Files.size(p); // Throws IOException if file is missing
Important
The Existence Tri-State Trap : In NIO.2, !Files.exists(p) is not always equivalent to Files.notExists(p) . If the JVM lacks security permissions to read the parent directory or verify the file's presence, both Files.exists(p) and Files.notExists(p) can return false simultaneously!
Identity Verification: Files.isSameFile()
Files.isSameFile(Path p1, Path p2) determines whether two paths locate the exact same file system entity:
If p1.equals(p2) evaluates to true , isSameFile() returns true immediately without accessing the file system .
If p1 and p2 are syntactically distinct, isSameFile() accesses the file system to compare unique operating system file identifiers (such as Unix inodes or Windows file index identifiers).
If either file does not exist on disk when disk lookup is required, an IOException ( NoSuchFileException ) is thrown. Path link = Path.of("/usr/bin/python");
Path target = Path.of("/usr/bin/python3.11");
// Returns true if /usr/bin/python is a symbolic link pointing to /usr/bin/python3.11:
- boolean same = Files.isSameFile(link, target);
- Creating Files and Directories
Method Target Behavior Exception Handling
Files.createFile(path) Creates a new empty file. Throws FileAlreadyExistsException if file exists; throws NoSuchFileException if parent is missing.
Files.createDirectory(path) Creates a single directory. Throws FileAlreadyExistsException if target exists; throws NoSuchFileException if parent directory is missing.
Files.createDirectories(path) Creates target and all missing parent directories. Does NOT throw if directory already exists. Creates nested directory trees safely.
Files.createTempFile(prefix, suffix) Creates temporary file in OS temp folder. Generates unique name (e.g. app-91823.tmp ).
Files.createTempDirectory(prefix) Creates temporary directory in OS temp folder. Generates unique folder. // Safely creating nested directory structures:
- Path deepDir = Path.of("/opt/app/data/logs/2026");
- Files.createDirectories(deepDir); // Creates all missing intermediate directories
- Copying and Moving Files Path src = Path.of("source.txt");
- Path dest = Path.of("backup.txt");
// 1. Copying with options:
- Files.copy(src, dest,
- StandardCopyOption.REPLACE_EXISTING,
- StandardCopyOption.COPY_ATTRIBUTES,
- LinkOption.NOFOLLOW_LINKS);
// 2. Moving / Renaming:
- Files.move(src, dest,
- StandardCopyOption.REPLACE_EXISTING,
- StandardCopyOption.ATOMIC_MOVE);
- Critical Exam Copy/Move Rules
Shallow Directory Copy Trap : When Files.copy() is invoked on a directory , it performs a shallow copy . It creates the destination directory, but it does NOT copy any of the files or subdirectories contained inside it! To copy a directory tree recursively, developers must walk the tree and copy each entry.
Directory Move Semantics : Moving a non-empty directory on the same file system volume succeeds and moves the directory along with all its children (by updating file system directory pointers). Moving a non-empty directory across different volumes throws DirectoryNotEmptyException unless copied recursively.
ATOMIC_MOVE : The StandardCopyOption.ATOMIC_MOVE option guarantees that the move operation executes as an atomic file system transaction. If the underlying operating system or drive configuration does not support atomic moves (e.g., across separate physical drives), AtomicMoveNotSupportedException is thrown.
Stream Interoperability :
Files.copy(InputStream in, Path target, CopyOption... options) : Writes all bytes from an input stream to a file.
Files.copy(Path source, OutputStream out) : Writes all bytes from a file to an output stream.
Deleting Files and Directories
Files.delete(Path path) : Deletes the target file or directory. Throws NoSuchFileException if the path does not exist. Throws DirectoryNotEmptyException if the target is a non-empty directory.
Files.deleteIfExists(Path path) : Returns true if the file/directory existed and was deleted; returns false if the path did not exist. Throws DirectoryNotEmptyException if the directory is non-empty.
6. Convenience Helper Methods for Small Files (Java 11+)
For small-to-medium files, NIO.2 provides high-level convenience methods on Files that manage stream lifecycle and buffering automatically: Path configPath = Path.of("config.json");
// 1. Read entire file into a String (Java 11+):
String jsonContent = Files.readString(configPath, StandardCharsets.UTF_8);
// 2. Write String directly to a file (Java 11+):
- Files.writeString(configPath, jsonContent, StandardCharsets.UTF_8,
- StandardOpenOption.CREATE,
- StandardOpenOption.WRITE,
- StandardOpenOption.TRUNCATE_EXISTING);
// 3. Read all lines into a List<String>:
List<String> lines = Files.readAllLines(configPath, StandardCharsets.UTF_8);
// 4. Read / Write raw byte arrays:
- byte[] rawBytes = Files.readAllBytes(configPath);
- Files.write(configPath, rawBytes, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
- StandardOpenOption Enumeration Flags
StandardOpenOption.READ : Open for read access.
StandardOpenOption.WRITE : Open for write access.
StandardOpenOption.CREATE : Create a new file if it does not exist.
StandardOpenOption.CREATE_NEW : Create a new file, failing with FileAlreadyExistsException if the file already exists.
StandardOpenOption.TRUNCATE_EXISTING : Truncate the file to 0 bytes if it already exists.
StandardOpenOption.APPEND : Append new data to the end of the existing file.