Read a File Line by Line in Java
Problem
Reading line by line keeps only the current line in memory, so it works for files of any size — unlike loading the whole file at once.
Read a short text file and print each line prefixed with its line number.
Java Program
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
public class ReadFileLineByLine {
public static void main(String[] args) throws IOException {
Path file = Paths.get("poem.txt");
Files.write(file, Arrays.asList("Roses bloom in May,", "Rivers find the sea,", "Code compiles today.")); // sample input
try (BufferedReader reader = new BufferedReader(new FileReader(file.toFile()))) {
String line;
int number = 1;
while ((line = reader.readLine()) != null) { // null means end of file
System.out.println(number++ + ": " + line);
}
}
Files.delete(file);
}
}Output
Core Logic
readLine() returns the next line each time it's called and null at the end of the file, which makes a natural loop condition.
- 1The program first writes a three-line
poem.txtso it has something to read. - 2
new BufferedReader(new FileReader(...))opens the file inside try-with-resources. - 3
while ((line = reader.readLine()) != null)reads and tests in one expression. - 4Each line is printed with a running line number, then the file is deleted.
readLine() returns Code compiles today., and the fourth returns null, ending the loop.Key Point: readLine() strips the line break, so the strings you receive never end in \n or \r\n.
Why: Every character is read once, and only the current line of length L is held at a time.
Key Concepts
Approach 2: Using Files.lines()
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.stream.Stream;
public class ReadFileLinesStream {
public static void main(String[] args) throws IOException {
Path file = Paths.get("poem.txt");
Files.write(file, Arrays.asList("Roses bloom in May,", "Rivers find the sea,", "Code compiles today."));
try (Stream<String> lines = Files.lines(file)) { // lazily reads lines; must be closed
lines.filter(line -> line.startsWith("R"))
.forEach(System.out::println);
}
Files.delete(file);
}
}
Output
Core Logic
Files.lines exposes the file as a lazy Stream<String>, so stream operations such as filter apply directly to its lines.
- 1
Files.lines(file)opens the file and streams it one line at a time. - 2The stream is opened in try-with-resources, because it holds an open file handle.
- 3
filter(line -> line.startsWith("R"))keeps only lines beginning with R.
Key Point: Forgetting to close a Files.lines stream leaks the file handle — always open it in try-with-resources.
Why: Lines are read lazily, one at a time, as the stream pulls them.
Key Concepts
Interview Questions
How do you read a file with a specific character encoding?
Pass the charset: Files.newBufferedReader(path, StandardCharsets.UTF_8). Relying on the platform default can garble non-English text.
What's the easiest way to read a small file at once?
Files.readString(path) (Java 11+) or Files.readAllLines(path). Both load the whole file into memory, so keep them for small files.