Study guide
14.4 Advanced NIO.2: Directory Walking, Streams (`Files.lines`, `walk`), and `WatchService`
Oracle Java SE 17 Developer Certified Professional 1Z0-829
Track your progress as you read
Finished sections get a check mark.
1 Introduction: The 1Z0-829 Exam
About the Java SE 17 Developer (1Z0-829) Exam
2 Chapter 1: Java Fundamentals & Language Basics
1.1 Primitive Types, Literals, Wrapper Classes, and Type Promotion/Casting 1.2 Local Variable Type Inference (`var`) Rules and Scoping 1.3 Operators, Precedence, Evaluation Order, and Short-Circuit Evaluation 1.4 Text Blocks, String Immutability, and StringBuilder APIs
3 Chapter 2: Program Flow & Decision Constructs
2.1 Conditional Branching with if/else and the Ternary Operator 2.2 Switch Statements and Switch Expressions 2.3 Iteration Constructs, Loop Control, and Branching Labels
4 Chapter 3: Object-Oriented Design & Class Mechanics
3.1 Class Declarations, Field/Method Signatures, and Access Modifiers 3.2 Constructors, Overloading, Initialization Order, and Garbage Collection 3.3 Inheritance, Method Overriding, Polymorphism, and `super`/`this` 3.4 Abstract Classes, Static Members, and Object Methods (`equals`, `hashCode`, `toString`)
5 Chapter 4: Advanced OOP: Interfaces, Records & Sealed Types
4.1 Interface Architecture: Default, Static, and Private Methods 4.2 Enumerations: Constants, Constructors, Methods, and Custom Bodies 4.3 Records: Canonical/Compact Constructors, Components, and Immutability 4.4 Sealed Classes and Interfaces: `permits`, Hierarchy Constraints, and Modifiers
6 Chapter 5: Pattern Matching & Nested Types
5.1 Pattern Matching for `instanceof` and Flow Scoping Mechanics 5.2 Nested Types: Member Inner, Static Nested, Local, and Anonymous Classes 5.3 Defensive Programming, Immutability Patterns, and Validation
7 Chapter 6: Generics, Collections & Arrays
6.1 Generic Classes, Interfaces, Methods, and Type Erasure 6.2 Bounded Type Parameters and Wildcards (PECS Rule) 6.3 Collections Framework: List, Set, Queue, and Deque APIs 6.4 The Map Interface, Sorting, Comparable, and Comparator 6.5 Arrays: Declaration, Initialization, Multidimensional Arrays, and the Arrays Utility Class
8 Chapter 7: Functional Programming & Lambda Expressions
7.1 Functional Interfaces and Lambda Syntax Rules 7.2 Standard Functional Interfaces in `java.util.function` 7.3 Method References: Static, Bound, Unbound, and Constructor References
9 Chapter 8: The Stream API
8.1 Stream Lifecycle: Creation, Pipeline Architecture, and Lazy Evaluation 8.2 Intermediate Stream Operations: Filtering, Mapping, Sorting, and Slicing 8.3 Terminal Stream Operations: Reduction, Matching, Finding, and Collecting 8.4 Primitive Streams (`IntStream`, `LongStream`, `DoubleStream`) and Numeric Statistics
10 Chapter 9: Advanced Streams & Collectors
9.1 The Optional Class: Safe Null Handling, Transformation, and Chaining 9.2 The Collectors API: Downstream Reductions, Grouping, and Partitioning 9.3 Parallel Streams: Fork/Join Pool, Spliterators, and Concurrency Pitfalls
11 Chapter 10: Exception Handling
10.1 The Java Exception Hierarchy: Checked vs. Unchecked Exceptions and Errors 10.2 Exception Constructs: `try-catch-finally`, Multi-Catch, and Rethrowing 10.3 Try-With-Resources, `AutoCloseable`, Suppressed Exceptions, and Custom Exceptions
12 Chapter 11: Date/Time API & Localization
11.1 Date and Time Core Types: `LocalDate`, `LocalTime`, `LocalDateTime` 11.2 Timestamps, Intervals, and Durations: `Instant`, `Period`, and `Duration` 11.3 Time Zones, Daylight Saving Time (DST) Offsets, and `ZonedDateTime` 11.4 Internationalization and Localization: `Locale`, `ResourceBundle`, and Formatters 11.5 Formatting and Parsing Messages, Numbers, Percentages, and Localized Dates
13 Chapter 12: Java Platform Module System (JPMS)
12.1 Module Architecture, `module-info.java`, and Module Paths 12.2 Module Directives: `requires`, `requires transitive`, `exports`, and Qualified Exports 12.3 Reflection, Deep Encapsulation, `opens`, and Open Modules 12.4 Services in JPMS (`uses`, `provides...with`), Migration Paths, and Tooling (`jlink`, `jdeps`) 12.5 Compiling and Packaging Code: Non-Modular JARs, Modular JARs, and Migration Strategies
14 Chapter 13: Concurrency & Multithreading
13.1 Thread Fundamentals, `Runnable`, `Callable`, and Thread Lifecycle 13.2 Memory Visibility, `volatile`, Intrinsic Locks, and Synchronization 13.3 The Concurrency Framework: `ExecutorService`, Thread Pools, and `Future` 13.4 Concurrent Collections, Atomic Variables, Explicit Locks, and `CompletableFuture`
15 Chapter 14: Java I/O and NIO.2
14.1 Classical I/O: Byte and Character Streams, Readers, Writers, and Buffering 14.2 Object Serialization, `Serializable`, `transient`, and `serialVersionUID` 14.3 NIO.2 Architecture: `Path`, `Paths`, and Basic `Files` Operations 14.4 Advanced NIO.2: Directory Walking, Streams (`Files.lines`, `walk`), and `WatchService`
16 Chapter 15: Accessing Databases with JDBC
15.1 JDBC Architecture, Drivers, `DriverManager`, and `DataSource` Connections 15.2 SQL Execution: `Statement`, `PreparedStatement`, and `CallableStatement` 15.3 `ResultSet` Navigation, Data Types, and Transaction Management (Commit, Rollback, Savepoints)
17 Chapter 16: Annotations & the Java Logging API
16.1 Standard Annotations: @Override, @FunctionalInterface, @Deprecated, @SuppressWarnings, and @SafeVarargs 16.2 Java Logging API Basics: Logger, Levels, Handlers, and Configuration
14.4 Advanced NIO.2: Directory Walking, Streams (`Files.lines`, `walk`), and `WatchService`
Key Takeaways Explain This Topic AI
Files.list, Files.walk, Files.find, and Files.lines return lazy, functional streams backed by operating system file descriptors that must be enclosed in try-with-resources to prevent resource leaks.
Files.list(Path) performs a shallow single-level directory listing, while Files.walk(Path, maxDepth) performs a recursive depth-first traversal without following symbolic links by default.
Files.lines(Path) reads file lines lazily as a Stream<String> , avoiding the high memory footprint of Files.readAllLines(Path) on multi-gigabyte files.
BasicFileAttributes provides a read-only bulk snapshot of file metadata (size, timestamps, type flags), while BasicFileAttributeView is required to modify file timestamps.
Files.walk(start, maxDepth) counts the start path at depth 0, so walk(dir, 1) returns the directory plus its immediate entries while Files.list(dir) returns only the entries.
Last updated: October 2026
14.4 Advanced NIO.2: Directory Walking, Streams ( Files.lines , walk ), and WatchService
NIO.2 integrates seamlessly with the Java Stream API, providing lazy, functional methods on java.nio.file.Files for directory listing, recursive tree traversal, targeted file searching, and line-by-line file reading. Additionally, NIO.2 provides high-performance file attribute inspection through BasicFileAttributes and native operating system change notifications via WatchService . Exam questions focus on the resource lifecycle of these streams, lazy evaluation, and depth limits; WatchService is covered at the end as background because it is not named in the current objectives.
Functional Stream Methods in Files
Files includes four core methods that return java.util.stream.Stream . Because these streams interact directly with underlying file systems, understanding their depth, traversal order, and filtering behavior is vital. NIO.2 Stream Methods
- |
- +-----------------+--------+--------+-----------------+
- | | | |
- Files.list() Files.walk() Files.find() Files.lines()
- (Depth = 1) (Depth-First) (Depth + Filter) (Lazy Lines)
- 1. Files.list(Path dir)
Returns a Stream<Path> of entries located directly in the specified directory:
Shallow : Traverses only the immediate children (equivalent to depth 1). It does not recurse into subdirectories.
Does not include . or .. entries.
Throws NotDirectoryException if dir is not a directory. try (Stream<Path> stream = Files.list(Path.of("/var/log"))) {
- stream.forEach(System.out::println);
- }
- 2. Files.walk(Path start, int maxDepth, FileVisitOption... options)
Returns a Stream<Path> populated by depth-first recursive traversal starting at start :
Overloaded version Files.walk(Path start) defaults maxDepth to Integer.MAX_VALUE .
A maxDepth of 0 returns a stream containing only the starting path itself. A maxDepth of 1 matches the behavior of Files.list() plus the root.
Symbolic Links : By default, Files.walk does not follow symbolic links . To follow links, pass FileVisitOption.FOLLOW_LINKS .
Warning
The Circular Link Loop Trap When FileVisitOption.FOLLOW_LINKS is enabled, circular symbolic link cycles (a link pointing to an ancestor folder) can cause infinite traversal loops. If NIO.2 detects a cycle during Files.walk , the stream throws an UncheckedIOException whose cause is a java.nio.file.FileSystemLoopException .
3. Files.find(Path start, int maxDepth, BiPredicate<Path, BasicFileAttributes> matcher, FileVisitOption... options)
Performs recursive traversal while applying an attribute-aware filter in a single optimized pass:
Takes a BiPredicate<Path, BasicFileAttributes> allowing simultaneous filtering on the Path and its underlying file metadata.
Avoids the performance overhead of querying file attributes separately inside a .filter() call. Path rootDir = Path.of("/data/reports");
- try (Stream<Path> stream = Files.find(rootDir, 5,
- (path, attrs) -> attrs.isRegularFile() &&
- attrs.size() > 1_000_000 &&
- path.toString().endsWith(".pdf"))) {
- stream.forEach(p -> System.out.println("Large report: " + p));
- }
- 4. Files.lines(Path path)
Reads all lines from a file lazily as a Stream<String> using UTF-8 (or a specified Charset ):
Lazy streaming : Lines are read from disk only as demanded by downstream intermediate and terminal operations.
Contrast with Files.readAllLines(Path) :
Files.readAllLines(Path) reads the entire file eagerly into memory, returning a List<String> . Calling this on a 10 GB log file triggers OutOfMemoryError .
Files.lines(Path) streams lines lazily, processing gigabyte-scale files with minimal, constant heap memory consumption.
Mandatory Resource Management: Closing I/O Streams
A critical exam topic is resource leaks in NIO.2 streams. Standard Java in-memory streams (such as List.stream() ) do not hold external operating system handles and do not require closing.
However, all streams returned by Files.list() , Files.walk() , Files.find() , and Files.lines() implement AutoCloseable and hold open operating system file handles, directory iterators, or file channel locks.
Terminal stream operations (such as .count() , .findFirst() , or .collect() ) DO NOT close the underlying stream !
If an application repeatedly opens streams via Files.lines() or Files.walk() without closing them, the operating system eventually exhausts its file descriptor table, resulting in java.io.IOException: Too many open files .
Always enclose NIO.2 streams in a try-with-resources statement : // CORRECT: Try-with-resources guarantees file descriptor release
- try (Stream<String> lines = Files.lines(Path.of("/var/log/app.log"))) {
- long errorCount = lines.filter(l -> l.contains("ERROR")).count();
- System.out.println("Errors: " + errorCount);
- }
- File Attributes: BasicFileAttributes vs. BasicFileAttributeView
Querying individual file properties using individual static methods (such as Files.size() , Files.isDirectory() , Files.getLastModifiedTime() ) requires a separate round-trip system call for each property.
NIO.2 provides bulk attribute retrieval through attribute views in java.nio.file.attribute : Attribute Architecture
- |
- +-----------------------+-----------------------+
- | |
- [Read-Only Snapshot] [Mutable View]
- BasicFileAttributes BasicFileAttributeView
- (size, creationTime, isDirectory) (setTimes(mod, acc, create))
- BasicFileAttributes (Read-Only Bulk Snapshot)
Retrieves standard file system metadata in a single native system call via Files.readAttributes() : Path file = Path.of("data.txt");
BasicFileAttributes attrs = Files.readAttributes(file, BasicFileAttributes.class);
- long size = attrs.size(); // File size in bytes
- FileTime created = attrs.creationTime(); // Creation timestamp
- FileTime modified = attrs.lastModifiedTime(); // Last modified timestamp
- boolean isReg = attrs.isRegularFile(); // True if standard file
- boolean isDir = attrs.isDirectory(); // True if directory
- boolean isSym = attrs.isSymbolicLink(); // True if symbolic link
- boolean isOther = attrs.isOther(); // True if device, pipe, socket
BasicFileAttributes is strictly read-only ; it contains getter methods only.
BasicFileAttributeView (Mutable View)
To modify file system metadata, obtain a view instance via Files.getFileAttributeView() : BasicFileAttributeView view = Files.getFileAttributeView(file, BasicFileAttributeView.class);
FileTime newTime = FileTime.fromMillis(System.currentTimeMillis());
- // Update timestamps: lastModifiedTime, lastAccessTime, createTime
- // Passing null preserves the existing timestamp for that attribute
- view.setTimes(newTime, null, null);
Other specialized attribute views include DosFileAttributeView (for DOS hidden, archive, readonly flags) and PosixFileAttributeView (for Unix POSIX permissions and ownership).
Asynchronous File Monitoring: WatchService (Background)
Note
WatchService is not named in the current 1Z0-829 objectives; treat this subsection as background knowledge.
The WatchService API provides asynchronous notifications when registered directories experience file system events. Where the platform supports it, the JDK uses native event hooks (such as Linux inotify or Windows ReadDirectoryChangesW ); on other platforms, such as macOS, it falls back to polling.
- Setting Up a WatchService import java.nio.file.*;
- import static java.nio.file.StandardWatchEventKinds.*;
- public class DirectoryMonitor {
- public static void watchDirectory(Path dir) throws Exception {
- // Step 1: Create WatchService from default FileSystem
- try (WatchService watcher = FileSystems.getDefault().newWatchService()) {
- // Step 2: Register directory with event kinds
- dir.register(watcher, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
- // Step 3: Event listening loop
- while (true) {
- // Step 4: Retrieve signaled key (take blocks until event arrives)
- WatchKey key = watcher.take();
- // Step 5: Process queued events
- for (WatchEvent<?> event : key.pollEvents()) {
- WatchEvent.Kind<?> kind = event.kind();
- // StandardWatchEventKinds.OVERFLOW signals lost/dropped events
- if (kind == OVERFLOW) continue;
- // Context for entry events is a relative Path (filename)
- WatchEvent<Path> ev = (WatchEvent<Path>) event;
- Path filename = ev.context();
- System.out.printf("Event %s on file %s%n", kind.name(), filename);
- }
- // Step 6: CRUCIAL - Reset key to receive further events
- boolean valid = key.reset();
- if (!valid) {
- System.out.println("Monitored directory is no longer accessible");
- break;
- }
- }
- }
- }
- }
- Key Methods for Event Retrieval
watcher.poll() : Non-blocking. Returns the next available WatchKey , or null if no events have occurred.
watcher.poll(long timeout, TimeUnit unit) : Blocks up to the specified timeout waiting for a key to be signaled.
watcher.take() : Blocks indefinitely until a key is signaled or the thread is interrupted.
The key.reset() State Machine Rule
When events occur in a registered directory, the associated WatchKey transitions from the ready state to the signaled state and is queued in the WatchService .
Warning
The WatchKey.reset() Trap Once a WatchKey is signaled, no further events will be queued for that key until key.reset() is explicitly called! If you process events but forget to invoke key.reset() , the directory remains in the signaled state, and your application will never receive another notification for that folder. If key.reset() returns false , the directory has been deleted or has become inaccessible, and the loop should terminate.