LearnFrenzy
Sign in Take a free test

Files and Date-Time · Lesson 37 of 42

File Handling with java.nio.file

6 min read Updated Nov 7, 2026 Saurabh Samir

Programs need to keep data after they stop: reports, logs, exports, configuration. Modern Java handles files with the java.nio.file package – Path for locations and Files for operations. It is shorter and safer than the old FileReader/FileWriter code you will still see in older tutorials.

In this lesson

  • Path and Files: the modern API
  • Writing and reading whole files and line by line
  • Directories, checks, copying and deleting
  • A real task: summarising a CSV file

Write and read a file

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

public class Main {
    public static void main(String[] args) throws IOException {
        Path file = Path.of("notes.txt");
        Files.writeString(file, "Day 1: installed the JDK\nDay 2: variables\n");     // create / overwrite
        Files.writeString(file, "Day 3: loops\n", java.nio.file.StandardOpenOption.APPEND);

        String all = Files.readString(file);
        System.out.print(all);
        List<String> lines = Files.readAllLines(file);
        System.out.println(lines.size() + " lines, first: " + lines.get(0));
        System.out.println(Files.exists(file) + " " + Files.size(file) + " bytes");
    }
}

Output

Day 1: installed the JDK
Day 2: variables
Day 3: loops
3 lines, first: Day 1: installed the JDK
true 55 bytes
  • File operations throw IOException (a checked exception), so methods must catch or declare it. Here main declares throws IOException to keep the example short.
  • writeString and readString use UTF-8 by default – ₹, é and Indian scripts work correctly.
  • A relative path like "notes.txt" is resolved against the folder you ran the program from.

Large files: read line by line

readAllLines loads the entire file into memory. For big logs, stream the lines so only a little is in memory at once:

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) throws IOException {
        Path log = Path.of("server.log");
        Files.writeString(log, "INFO start\nERROR db timeout\nINFO login asha\nERROR payment failed\nWARN disk 85%\n");

        try (Stream<String> lines = Files.lines(log)) {               // closed automatically
            System.out.println(lines.filter(l -> l.startsWith("ERROR")).count() + " errors");
        }

        try (BufferedReader reader = Files.newBufferedReader(log)) {   // classic loop style
            String line;
            int n = 0;
            while ((line = reader.readLine()) != null) {
                n++;
                if (line.startsWith("WARN")) System.out.println("line " + n + ": " + line);
            }
        }
    }
}

Output

2 errors
line 5: WARN disk 85%

Common mistake: not closing file streams

Files.lines(...) and newBufferedReader(...) hold an open file handle. Always use them in try-with-resources; otherwise long-running servers eventually fail with "too many open files".

Writing many lines

import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

public class Main {
    public static void main(String[] args) throws IOException {
        List<String> tasks = List.of("buy milk", "call bank", "finish chapter 14");
        Path todo = Path.of("todo.txt");
        Files.write(todo, tasks);                                  // one element per line

        try (BufferedWriter w = Files.newBufferedWriter(Path.of("report.txt"))) {
            for (int i = 0; i < tasks.size(); i++) {
                w.write((i + 1) + ". " + tasks.get(i));
                w.newLine();
            }
        }
        System.out.print(Files.readString(Path.of("report.txt")));
    }
}

Output

1. buy milk
2. call bank
3. finish chapter 14

Directories and file operations

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) throws IOException {
        Path dir = Path.of("exports", "2026");
        Files.createDirectories(dir);                         // creates parents too
        Path a = dir.resolve("sales.csv");                    // exports/2026/sales.csv
        Files.writeString(a, "id,amount\n1,500\n");
        Path b = dir.resolve("sales-backup.csv");
        Files.copy(a, b, StandardCopyOption.REPLACE_EXISTING);

        System.out.println(a.getFileName() + " | parent: " + a.getParent());
        try (Stream<Path> files = Files.list(dir)) {
            files.map(p -> p.getFileName().toString()).sorted().forEach(System.out::println);
        }
        Files.delete(b);
        System.out.println(Files.exists(b));
    }
}

Output

sales.csv | parent: exports/2026
sales-backup.csv
sales.csv
false

On Windows the parent prints as exports\2026; Path uses the right separator for each system, so never hard-code "/" or "\\" when building paths – use Path.of(a, b) or resolve.

A real task: summarise a CSV

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

public class Main {
    public static void main(String[] args) throws IOException {
        Path csv = Path.of("sales.csv");
        Files.writeString(csv, """
            region,rep,amount
            North,Asha,1200
            South,Ben,800
            North,Chen,450
            South,Dia,1500
            East,Eli,not-a-number
            """);

        Map<String, Integer> totals = new TreeMap<>();
        List<String> lines = Files.readAllLines(csv);
        for (String line : lines.subList(1, lines.size())) {      // skip the header
            String[] f = line.split(",");
            try {
                totals.merge(f[0], Integer.parseInt(f[2]), Integer::sum);
            } catch (NumberFormatException e) {
                System.out.println("Skipped bad row: " + line);
            }
        }
        System.out.println(totals);
        System.out.println("Grand total: " + totals.values().stream().mapToInt(Integer::intValue).sum());
    }
}

Output

Skipped bad row: East,Eli,not-a-number
{North=1650, South=2300}
Grand total: 3950

For real CSV files – with quoted commas, for example – use a library such as OpenCSV or Apache Commons CSV. For JSON, Spring Boot uses Jackson.

Practice

Try it yourself

  1. Write the numbers 1–5 to numbers.txt, then read them back and print their sum.

    Show solution
    import java.io.IOException;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.util.List;
    
    public class Main {
        public static void main(String[] args) throws IOException {
            Path p = Path.of("numbers.txt");
            Files.write(p, List.of("1", "2", "3", "4", "5"));
            int sum = Files.readAllLines(p).stream().mapToInt(Integer::parseInt).sum();
            System.out.println(sum);
        }
    }

    Output

    15

Quick check

Which exception do most file methods throw?

IOException – a checked exception.

Why use Files.lines instead of readAllLines for a 5 GB log?

It streams lines lazily instead of loading the whole file into memory.

How do you build "exports/2026/sales.csv" portably?

Path.of("exports", "2026", "sales.csv") or dir.resolve("sales.csv").

Summary

  • Use Path for locations and Files for operations; they default to UTF-8.
  • readString/readAllLines for small files; Files.lines or BufferedReader in try-with-resources for big ones.
  • createDirectories, resolve, copy, list and delete cover everyday file management.

Finished this lesson?

Mark it complete to track your progress, then carry on with the next lesson.

Next lesson

Popular