How to Read a File Line by Line in Java
Read a Java file line by line with BufferedReader, Files.lines, Files.readAllLines, and Scanner.
How to Read a File Line by Line in Java
Reading a text file one line at a time is one of the most common file tasks in Java. The JDK gives you several ways to do it; the right choice depends on the file's size and whether you want a plain loop or a stream pipeline. This chapter shows the four idiomatic approaches and when to reach for each.
BufferedReader: the streaming workhorse
BufferedReader.readLine() reads a single line per call and returns null at end of file, so it pairs naturally with a while loop. Wrap it in try-with-resources so the underlying reader closes itself:
Path file = Path.of("notes.txt");
try (BufferedReader br = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}This streams the file: only one line is held in memory at a time, so it handles a multi-gigabyte log without trouble. Files.newBufferedReader defaults to UTF-8, but passing the charset explicitly documents intent and avoids platform-dependent decoding. Note that readLine() strips the line terminator (\n, \r, or \r\n), so you never see it in the returned string.
Files.lines: the same job as a Stream
When you want a functional pipeline — filter, map, count — Files.lines gives you a lazy Stream<String> over the file's lines:
try (Stream<String> lines = Files.lines(Path.of("notes.txt"), StandardCharsets.UTF_8)) {
lines.filter(s -> !s.isBlank())
.map(String::trim)
.forEach(System.out::println);
}Like BufferedReader, it reads lazily and never loads the whole file. The catch is that the stream holds an open file handle, so it must be closed — always use it inside try-with-resources, never as a bare expression. Forgetting this leaks descriptors.
Files.readAllLines: small files, all at once
If the file is small and you want every line in a List<String> up front, Files.readAllLines is the most direct option:
List<String> all = Files.readAllLines(Path.of("notes.txt"), StandardCharsets.UTF_8);
for (String line : all) {
System.out.println(line);
}It is eager: the entire file is decoded into memory before you touch the first line. That is convenient for config files and fixtures but a poor fit for large files — prefer the streaming approaches there. Scanner with nextLine() and hasNextLine() is a fourth option, handy when you also need to parse tokens, but it is slower and easy to misuse, so the three above cover most cases.
| Approach | Memory | Returns | Best for |
|---|---|---|---|
BufferedReader.readLine() | One line | Plain loop | Large files, manual control |
Files.lines() | One line (lazy) | Stream<String> | Pipelines on large files |
Files.readAllLines() | Whole file | List<String> | Small files, random access |
Scanner.nextLine() | One line | Plain loop | Mixed line + token parsing |
Worked example: all three side by side
This program writes a tiny temp file (so it is self-contained), then reads it three ways — a BufferedReader loop, a Files.lines stream, and an eager Files.readAllLines:
What to take from the run:
- The
BufferedReaderloop numbers four lines, and line3: []shows that a blank line in the file is returned as an empty string, not skipped —readLine()reports every line, including empty ones. readLine()printed each line without any trailing\n, confirming it strips the line terminator; the only brackets around the text are the literal[and]the code added.Files.linescountednon-blank lines: 3because thefilter(s -> !s.isBlank())dropped the empty line — the stream pipeline operates lazily over the same four lines the loop saw.Files.readAllLinesreportedtotal lines: 4and afirst line : alpha, proving it loaded the whole file eagerly into aList<String>you can index withget(0).- Each reader sat inside try-with-resources (or returned a managed
List), so the file handle and temp file were released cleanly beforedoneprinted — no leaked descriptors.
Practice
Why must a stream returned by Files.lines() be used inside a try-with-resources block?