Java BufferedReader Tutorial: Read Files and Console Input Efficiently

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

MethodReturnsDescription
readLine()StringReads a line of text; returns null at end of stream.
read()intReads a single character; returns -1 at end of stream.
read(char[], off, len)intReads characters into a portion of an array; returns number of chars read or -1.
skip(long n)longSkips n characters.
ready()booleanReturns true if the stream is ready to be read without blocking.
mark(int readAheadLimit)voidMarks the current position in the stream.
reset()voidResets the stream to the most recent mark.
lines()Stream<String>Returns lines as a lazy Stream (Java 8+).
close()voidCloses the stream and releases resources.

BufferedReader vs Scanner vs Files.readAllLines()

FeatureBufferedReaderScannerFiles.readAllLines()
PerformanceExcellentGoodGood (loads all lines)
Memory usageStreaming (low)Streaming (low)Loads entire file into RAM
Parsing tokensManualBuilt-in (nextInt() etc.)Manual
Best forLarge files, high-speed readingTokenised input, user promptsSmall 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 incorrectlyreadLine() returns null (not an empty string) at end of file. Use while ((line = br.readLine()) != null).
  • Charset issuesFileReader uses the platform default charset. For consistent behaviour use new 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

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.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.