BufferedReader is the go-to class for reading text efficiently in Java. By wrapping any Reader in a buffer, it dramatically reduces the number of low-level I/O calls needed, making it orders of magnitude faster than reading one character at a time. In this tutorial you will learn how BufferedReader works under the hood, master every important method with annotated examples, and see the modern try-with-resources pattern that guarantees the stream is always closed properly.
What Is BufferedReader?
java.io.BufferedReader is a character-stream reader that reads text from a character-input stream and buffers characters in an internal char array (default size 8,192 characters). Applications that read text from files, consoles, or network streams should almost always wrap the underlying reader in a BufferedReader to avoid reading one character per system call.
Constructors
// Default buffer size (8192 chars)
BufferedReader br = new BufferedReader(new FileReader("file.txt"));
// Custom buffer size (e.g., 16 KB for large files)
BufferedReader br = new BufferedReader(new FileReader("file.txt"), 16384);
Reading a File Line by Line
The most common use case is reading a text file line by line using readLine(). Always use try-with-resources so the reader is closed automatically even if an exception is thrown:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class ReadFileExample {
public static void main(String[] args) {
String filePath = "sample.txt";
// try-with-resources closes the reader automatically
try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
String line;
int lineNumber = 1;
// readLine() returns null at end of file
while ((line = br.readLine()) != null) {
System.out.printf("%3d: %s%n", lineNumber++, line);
}
} catch (IOException e) {
System.err.println("Error reading file: " + e.getMessage());
}
}
}
Reading Console Input
Wrap System.in via InputStreamReader to read typed user input efficiently:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
public class ConsoleInputExample {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.print("Enter your name: ");
String name = br.readLine();
System.out.print("Enter your age : ");
int age = Integer.parseInt(br.readLine().trim());
System.out.printf("Hello %s, you are %d years old.%n", name, age);
}
}
Reading All Lines into a List
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class ReadAllLinesExample {
public static List<String> readAllLines(String filePath) throws IOException {
List<String> lines = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = br.readLine()) != null) {
lines.add(line);
}
}
return lines;
}
public static void main(String[] args) throws IOException {
List<String> lines = readAllLines("sample.txt");
System.out.println("Total lines: " + lines.size());
lines.forEach(System.out::println);
}
}
Using the lines() Stream (Java 8+)
Java 8 added a lines() method that returns a Stream<String>, enabling functional-style processing:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
public class LinesStreamExample {
public static void main(String[] args) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader("data.csv"))) {
// Count lines that start with a digit
long count = br.lines()
.filter(line -> !line.isEmpty() && Character.isDigit(line.charAt(0)))
.count();
System.out.println("Data rows: " + count);
}
}
}
Important Methods Reference
| Method | Returns | Description |
|---|---|---|
readLine() | String | Reads a line of text; returns null at end of stream. |
read() | int | Reads a single character; returns -1 at end of stream. |
read(char[], off, len) | int | Reads characters into a portion of an array; returns number of chars read or -1. |
skip(long n) | long | Skips n characters. |
ready() | boolean | Returns true if the stream is ready to be read without blocking. |
mark(int readAheadLimit) | void | Marks the current position in the stream. |
reset() | void | Resets the stream to the most recent mark. |
lines() | Stream<String> | Returns lines as a lazy Stream (Java 8+). |
close() | void | Closes the stream and releases resources. |
BufferedReader vs Scanner vs Files.readAllLines()
| Feature | BufferedReader | Scanner | Files.readAllLines() |
|---|---|---|---|
| Performance | Excellent | Good | Good (loads all lines) |
| Memory usage | Streaming (low) | Streaming (low) | Loads entire file into RAM |
| Parsing tokens | Manual | Built-in (nextInt() etc.) | Manual |
| Best for | Large files, high-speed reading | Tokenised input, user prompts | Small files, convenience |
Sample Output
Enter your name: Alice
Enter your age : 30
Hello Alice, you are 30 years old.
Common Pitfalls
- Not closing the reader — always use try-with-resources or a finally block to prevent file handle leaks.
- Checking for null incorrectly —
readLine()returnsnull(not an empty string) at end of file. Usewhile ((line = br.readLine()) != null). - Charset issues —
FileReaderuses the platform default charset. For consistent behaviour usenew BufferedReader(new InputStreamReader(new FileInputStream(path), StandardCharsets.UTF_8)). - Ignoring the buffer size — the default 8,192-char buffer is fine for most uses, but increase it to 64 KB or higher when reading multi-gigabyte log files.
See Also
- Java Streams API: The Complete Reference Guide
- Java Collections Framework: Choosing the Right Data Structure
- Disarium Number in Java
Conclusion
BufferedReader remains one of the most efficient and reliable ways to read text in Java. It is ideal for large files where loading everything into memory is impractical, for reading console input in competitive programming, and for processing streams line by line. Pair it with the Java 8 lines() method for clean, expressive functional-style pipelines. Remember: always specify the charset explicitly and always close the reader with try-with-resources.