Going Deeper (Modern & Professional Java)Pro· 40 min read

Reading & Writing Files

Make data outlive your program: write text to a file and read it back, the safe modern way with try-with-resources.

What you will learn

  • Write text to a file
  • Read a file line by line
  • Use try-with-resources to close files automatically

Why files?

Everything our programs have stored so far vanishes the moment they stop — it lived only in memory. To make data last, we save it to a file on disk. Then a later run (or another program) can read it back. This is the simplest form of saving, or persistence.

Files come in two flavours: text files (human-readable, like .txt or .csv) and binary files (raw bytes, like images). We will focus on text, which covers most everyday needs.

Writing text to a file

To write, we use a FileWriter wrapped in a BufferedWriter (which makes writing efficient). Notice the file reading and writing classes can throw a checked IOException — the kind Java forces you to handle, which is why everything sits inside try / catch.

We open the writer inside the special try ( ... ) brackets. This is try-with-resources: any file opened there is closed automatically when the block ends, even if an error happens. No forgotten files left open.

Writing two lines to a file with try-with-resources
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter("notes.txt"))) {
            writer.write("First line");
            writer.newLine();              // move to a new line
            writer.write("Second line");
            writer.newLine();
            System.out.println("Saved to notes.txt");
        } catch (IOException e) {
            System.out.println("Could not write: " + e.getMessage());
        }
    }
}

Note: Output: Saved to notes.txt A file called notes.txt now exists next to your program, containing: First line Second line write added the text and newLine() ended each line. Because the writer was opened in the try ( ... ) brackets, it was closed automatically at the end — your data is safely flushed to disk with no clean-up code from you.

Watch out: A plain new FileWriter("notes.txt") overwrites the whole file every time. To add to the end instead, pass true as a second argument: new FileWriter("notes.txt", true) — this is called append mode.

Reading a file line by line

To read, we use a BufferedReader. Its readLine() method returns the next line of text each time, and returns the special value null when there are no more lines — that is how we know to stop.

Reading every line until readLine returns null
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        try (BufferedReader reader = new BufferedReader(new FileReader("notes.txt"))) {
            String line;
            int number = 1;
            while ((line = reader.readLine()) != null) {   // null means end of file
                System.out.println(number + ": " + line);
                number++;
            }
        } catch (IOException e) {
            System.out.println("Could not read: " + e.getMessage());
        }
    }
}

Note: Output: 1: First line 2: Second line The while loop called readLine() each round, storing the line and checking it is not null. When the file ran out of lines, readLine() returned null, the condition became false, and the loop stopped. We numbered the lines as we printed them. The reader closed itself at the end, thanks to try-with-resources.

The read/write flow, step by step

  1. Open the file inside try ( ... ) — a FileWriter/BufferedWriter to write, or a FileReader/BufferedReader to read.
  2. Do the workwrite lines, or loop with readLine() until it returns null.
  3. Handle failures in a catch (IOException e), because file operations are checked and can fail (missing file, no permission).
  4. Closing is automatic — try-with-resources shuts the file for you when the block ends, so the data is saved and no resource leaks.

Tip: For small files, modern Java offers an even shorter way: Files.writeString(Path.of("notes.txt"), "hi") and Files.readString(Path.of("notes.txt")) from java.nio.file. The Buffered approach shown here is the classic, widely-taught one and works line by line for any size.

Q. How does the reading loop know it has reached the end of the file?

Answer: readLine() returns the next line as a String, or null when there are no more lines. The loop condition (line = reader.readLine()) != null stops as soon as null comes back.

✍️ Practice

  1. Write a program that saves three of your favourite foods, one per line, to a file called foods.txt.
  2. Write a second program that reads foods.txt and prints each food with a number in front of it.

🏠 Homework

  1. Build a tiny note-keeper: ask the user for a note with Scanner and append it to diary.txt (append mode), then read the whole file back and print every saved note.
Want to learn this with a mentor?

CodingClave runs guided, project-based training (28-day, 45-day & 6-month batches).

Explore Training →