JV

Java Fundamentals

19 lessons

Progress0%
1. Introduction to Java
1What is Java?
2. Variables and Data Types
1Primitive Types
3. Control Flow
ConditionalsLoops
4. Methods
Defining MethodsMethod Overloading
5. Object-Oriented Programming
Classes and ObjectsInheritanceInterfaces and Abstract Classes
6. Collections
ArrayList and LinkedListHashMap and HashSet
7. Exception Handling
Checked & Unchecked Exceptionstry-with-resources & Custom Exceptions
8. Generics
Generic Classes & MethodsWildcards & Type Erasure
9. Modern Java
Lambdas & Functional InterfacesStream API & Optional
10. File I/O
java.nio.file APIBuffered I/O & try-with-resources
All Tutorials
JavaFile I/O
Lesson 18 of 19 min
Chapter 10 · Lesson 1

java.nio.file API

Java's java.nio.file package (NIO.2, introduced in Java 7) provides a modern, expressive API for filesystem operations. It supersedes the error-prone java.io.File class.

Path and Paths

Path represents a path in the filesystem (file or directory). Create one with:

  • Path.of("dir/file.txt") (Java 11+) — preferred
  • Paths.get("dir/file.txt") — equivalent, works from Java 7

Paths can be absolute or relative. Useful methods: resolve(), relativize(), normalize(), getParent(), getFileName(), toAbsolutePath().

Files utility class

The Files class provides static methods for virtually all common filesystem tasks:

Existence and metadata:

  • Files.exists(path) / Files.notExists(path)
  • Files.isDirectory(path) / Files.isRegularFile(path)
  • Files.size(path) — file size in bytes
  • Files.getLastModifiedTime(path)

Creating and deleting:

  • Files.createFile(path) — throws if file exists
  • Files.createDirectory(path) / Files.createDirectories(path) — create parent dirs too
  • Files.delete(path) — throws if not found; Files.deleteIfExists(path) — silent

Reading and writing (small files):

  • Files.readString(path) (Java 11+) — entire file as String
  • Files.writeString(path, content) (Java 11+) — write String to file
  • Files.readAllLines(path) — returns List<String>
  • Files.readAllBytes(path) / Files.write(path, bytes)

Copying and moving:

  • Files.copy(src, dest, CopyOption...)
  • Files.move(src, dest, CopyOption...)
  • StandardCopyOption.REPLACE_EXISTING, COPY_ATTRIBUTES, ATOMIC_MOVE

Walking directory trees:

  • Files.walk(startPath) — returns Stream<Path> of all descendants
  • Files.find(start, maxDepth, BiPredicate) — filtered walk
  • Files.list(dir) — non-recursive directory listing

StandardOpenOption

Used with write methods: CREATE, APPEND, TRUNCATE_EXISTING, CREATE_NEW (fail if exists).

Code Examples

Write and read a text filejava
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.List;

public class NioReadWriteDemo {
    public static void main(String[] args) throws IOException {
        Path file = Path.of("demo.txt");

        // Write entire string (creates or replaces file)
        Files.writeString(file, "Hello, NIO!\nLine two\nLine three");
        System.out.println("Written: " + Files.size(file) + " bytes");

        // Read back as a single String (Java 11+)
        String content = Files.readString(file);
        System.out.println("Content:\n" + content);

        // Read as list of lines
        List<String> lines = Files.readAllLines(file);
        System.out.println("Lines   : " + lines.size());
        lines.forEach(l -> System.out.println("  > " + l));

        // Append more content
        Files.writeString(file, "\nAppended line",
            StandardOpenOption.APPEND);
        System.out.println("After append: " + Files.readAllLines(file).size() + " lines");

        // Metadata
        System.out.println("Exists  : " + Files.exists(file));
        System.out.println("Is file : " + Files.isRegularFile(file));

        // Cleanup
        Files.deleteIfExists(file);
        System.out.println("Deleted : " + !Files.exists(file));
    }
}

Files.writeString and readString (Java 11+) handle the most common I/O tasks in one line each. StandardOpenOption.APPEND adds content without truncating. Always clean up temp files in examples.

Directory listing with Files.walkjava
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class DirectoryWalkDemo {
    public static void main(String[] args) throws IOException {
        // Create a temporary directory tree for the demo
        Path root = Files.createTempDirectory("demo-tree");
        Path sub  = Files.createDirectory(root.resolve("subdir"));
        Files.writeString(root.resolve("a.txt"),  "file a");
        Files.writeString(root.resolve("b.java"), "file b");
        Files.writeString(sub.resolve("c.txt"),   "file c");
        Files.writeString(sub.resolve("d.java"),  "file d");

        // Walk all entries (directories included)
        System.out.println("All entries:");
        try (Stream<Path> walk = Files.walk(root)) {
            walk.forEach(p -> System.out.println("  " + root.relativize(p)));
        }

        // Non-recursive listing of root only
        System.out.println("Root only:");
        try (Stream<Path> list = Files.list(root)) {
            list.forEach(p -> System.out.println("  " + p.getFileName()));
        }

        // Find only .java files (any depth)
        System.out.println(".java files:");
        try (Stream<Path> found = Files.find(root, 10,
                (p, attrs) -> attrs.isRegularFile()
                           && p.toString().endsWith(".java"))) {
            found.map(Path::getFileName).forEach(p -> System.out.println("  " + p));
        }

        // Count files by extension using streams
        try (Stream<Path> walk2 = Files.walk(root)) {
            long txtCount = walk2
                .filter(Files::isRegularFile)
                .filter(p -> p.toString().endsWith(".txt"))
                .count();
            System.out.println(".txt count: " + txtCount);
        }

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

Files.walk returns a lazy Stream<Path> — always close it in a try-with-resources to release the underlying directory handle. Files.find is more efficient when you have a filter condition, as it can prune branches early.

Copy file with StandardCopyOptionjava
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;

public class FileCopyDemo {
    public static void main(String[] args) throws IOException {
        // Setup: create source files
        Path source = Files.createTempFile("source", ".txt");
        Files.writeString(source, "Original content\nLine 2\nLine 3");

        Path dest    = source.resolveSibling("destination.txt");
        Path moved   = source.resolveSibling("moved.txt");

        // Copy — fails if dest exists; use REPLACE_EXISTING to overwrite
        Files.copy(source, dest, StandardCopyOption.REPLACE_EXISTING);
        System.out.println("Copied   : " + Files.readString(dest));

        // Verify source still exists after copy
        System.out.println("Source exists: " + Files.exists(source));

        // Move (rename) — ATOMIC_MOVE where supported
        Files.move(source, moved, StandardCopyOption.REPLACE_EXISTING);
        System.out.println("Source after move: " + Files.exists(source));
        System.out.println("Moved exists     : " + Files.exists(moved));
        System.out.println("Moved content    : " + Files.readString(moved));

        // Copy with attributes preserved (timestamps etc.)
        Path withAttrs = moved.resolveSibling("with-attrs.txt");
        Files.copy(moved, withAttrs,
            StandardCopyOption.REPLACE_EXISTING,
            StandardCopyOption.COPY_ATTRIBUTES);
        System.out.println("Attrs copy size: " + Files.size(withAttrs));

        // Cleanup
        Files.deleteIfExists(dest);
        Files.deleteIfExists(moved);
        Files.deleteIfExists(withAttrs);
        System.out.println("Cleanup done");
    }
}

Files.copy does NOT delete the source — use Files.move for renaming/relocating. REPLACE_EXISTING silently overwrites the destination. ATOMIC_MOVE guarantees that the rename is an indivisible operation on platforms that support it.

Quick Quiz

1. Which method creates all missing parent directories in a single call?

2. What does `Files.walk()` return?

3. Which StandardCopyOption should you use to overwrite an existing destination file?

4. Which Java version introduced `Files.readString()` and `Files.writeString()`?

Was this lesson helpful?

PreviousNext