skip to content

Reading & Writing Text Files

The everyday patterns for reading and writing text: buffered readers and writers, line-at-a-time iteration, and an explicit charset. Interviewers watch for whether you stream a large file or slurp it into memory.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

5

What is the simplest modern way to read all text from a small file and write text to a file in Java, and what should you watch out for?

level: juniorimportance: must knowfreq 70%

answer

  1. Files.readString / writeString (Java 11+)
  2. readAllLines -> List<String>
  3. UTF-8 default; whole file into memory
  4. small files only; write truncates unless APPEND
  5. throws IOException (checked)

basics

~20 s

Use the Files helper class: Files.readString(path) reads the whole file into one String, and Files.writeString(path, text) writes a String to a file. Both are fine for small files only, because they load everything into memory at once.

solid answer

~40 s

Since Java 11 the easiest way is java.nio.file.Files. Files.readString(Path) returns the entire file as one String, and Files.writeString(Path, CharSequence, OpenOption...) writes a String out. For lines, Files.readAllLines(Path) gives a List<String> and Files.write(Path, Iterable<String>) writes them back. These one-liners default to UTF-8 (Java 18+ makes UTF-8 the platform default too), and they open and close the file for you, so there is no resource leak. The big caveat: they read the whole file into memory, so they only suit small-to-moderate files. For large files you stream line by line with Files.lines or a BufferedReader. Writing replaces the file by default unless you pass StandardOpenOption.APPEND.

go deeper

for a junior

Knows Files.readString/writeString and readAllLines exist and that they handle small files end to end.

for a middle

Knows the UTF-8 default, the memory caveat, and write-truncates-by-default vs APPEND.

for a senior

Articulates the pre-Java-18 platform-default-charset pitfall and chooses streaming vs whole-file deliberately based on size.

for a principal

Sets team conventions (always explicit charset, size thresholds, OpenOption discipline) and understands the Java 18 UTF-8 default migration impact across the codebase.

## The problem Reading and writing text files is one of the most common I/O tasks. A *text file* is just bytes on disk that represent characters according to an *encoding* (a mapping between characters and byte sequences, e.g. UTF-8). To turn bytes into a Java String you must decode them with the right encoding; to write a String you encode it back to bytes. ## The tools Java has several layers: - **`java.io`** (old): `FileReader`/`FileWriter`, `BufferedReader`/`BufferedWriter` — stream-based, manual resource management. - **`java.nio.file`** (newer, since Java 7, expanded in 11): the **`Files`** utility class plus **`Path`** (an object describing a file location, created via `Path.of("...")` or `Paths.get("...")`). ## The easy helpers (Java 11+) ```java Path p = Path.of("notes.txt"); String all = Files.readString(p); // whole file -> one String Files.writeString(p, "hello\nworld\n"); // write a String, replacing the file List<String> lines = Files.readAllLines(p); // each line as a List entry Files.write(p, lines); // write a List of lines back ``` These methods **open the file, do the work, and close it automatically**, so you cannot leak a file handle. That is their main appeal over the old API where you had to close streams yourself. ## Encoding A *charset* is the character encoding. `Files.readString`/`writeString` use **UTF-8** by default. From **Java 18** UTF-8 is also the JVM-wide default charset (`file.encoding`), so older APIs like `FileReader` that previously used the platform default now also default to UTF-8. Before Java 18, the platform default could be Windows-1252 on Windows or UTF-8 on Linux, causing files to read differently on different machines — a classic bug. Always be explicit about charset when correctness matters. ## The big caveat: memory `readString` and `readAllLines` load the **entire file into memory**. For a multi-gigabyte log this throws `OutOfMemoryError`. They are for *small* files (config, small data). For large files, stream line by line (`Files.lines` or `BufferedReader`). ## Write semantics `Files.writeString`/`Files.write` **truncate and replace** the file by default (they pass `CREATE`, `WRITE`, `TRUNCATE_EXISTING`). To append, pass `StandardOpenOption.APPEND`. To fail if the file already exists, pass `StandardOpenOption.CREATE_NEW`. ## Checked exceptions All of these throw `java.io.IOException` (a *checked* exception — the compiler forces you to handle or declare it), so you wrap them in try/catch or add `throws IOException`.

  • When should you NOT use Files.readString?
    When the file may be large — it loads everything into memory and can throw OutOfMemoryError. Stream line by line instead.
  • How do you append instead of overwrite with Files.writeString?
    Pass StandardOpenOption.APPEND as a trailing OpenOption argument; otherwise the file is truncated and replaced.

saying these in an interview costs you the question

  • Using readString/readAllLines on huge files (OOM)
  • Assuming the platform default charset is always UTF-8 before Java 18
  • Forgetting these methods overwrite the file unless APPEND is passed
  • Thinking you must manually close anything with the Files helpers

context

open as a page

Why use BufferedReader/BufferedWriter when reading or writing text, and how do you create one correctly?

level: middleimportance: must knowfreq 68%

basics

~20 s

Buffering reads or writes data in big chunks instead of one character at a time, which is much faster because it makes far fewer slow disk/OS calls. You wrap a reader/writer, e.g. new BufferedReader(new FileReader(file)), and read line by line with readLine().

open as a page

Why is specifying a charset explicitly important when reading and writing text files in Java, and how does this differ across Java versions?

level: seniorimportance: must knowfreq 50%

basics

~20 s

A charset decides how characters become bytes and back. If you read a file with a different charset than it was written in, you get garbled text. Always pass StandardCharsets.UTF_8 explicitly so behavior is the same on every machine.

open as a page

How do you process a large text file line by line in a memory-efficient way using Files.lines, and what must you be careful about?

level: middleimportance: should knowfreq 55%

basics

~20 s

Use Files.lines(path), which gives a Stream<String> that reads lines lazily one at a time instead of loading the whole file. Wrap it in try-with-resources so the underlying file gets closed, because a stream from a file holds an open file handle.

open as a page

How would you write a text file so that a crash never leaves a half-written or corrupted file (atomic / durable writes)?

level: principalimportance: should knowfreq 30%

basics

~20 s

Write to a temporary file first, then rename it over the real file. Rename is atomic on the same filesystem, so readers either see the old file or the complete new one, never a half-written file. If a crash happens mid-write, only the temp file is damaged.

open as a page