Java • Input and Output

BufferedReader for Files — Read text files using BufferedReader (intro)

Learn how Java efficiently reads text files one line at a time using buffered input streams.

Java programming emblem with a coffee cup, code braces, and terminal
Inside this lesson 10 sections 5 questions 8 figures

SECTION 1

Historical Context & Motivation

Reading data from files has been a fundamental task in computing since the earliest days of programming. Early programs had to interact directly with hardware — reading one character at a time from tape drives or disk platters. This was painfully slow because every single read required the operating system to physically access the storage device. As programs grew more complex, developers realized they needed a smarter way to pull data from files without making the CPU wait after every character.

When Java was first released by Sun Microsystems in 1995, it included a rich set of Input/Output (I/O) classes in the java.io package. Among these was BufferedReader, a class designed to solve the performance problem of reading text one character at a time. Instead of going to the file for each character, BufferedReader grabs a large chunk of data at once and stores it in memory — a technique called buffering. This dramatically reduced the number of slow disk-access operations.

1970s • Buffered I/O Concept Emerges

Operating systems like UNIX introduce buffered streams in C's standard library (stdio.h) to reduce costly disk reads.

1995 • Java 1.0 Released

Sun Microsystems launches Java with the java.io package, including FileReader and BufferedReader for text-based file reading.

2004 • Java 5 Adds Scanner

The Scanner class arrives as an alternative for parsing input, but BufferedReader remains the go-to for raw speed when reading large text files.

2011 • Java 7 — Try-With-Resources

Java 7 introduces the try-with-resources statement, making BufferedReader much safer and easier to use by automatically closing file handles.

2014 • Java 8 — Streams Integration

BufferedReader gains the lines() method, connecting it to Java's modern Stream API for functional-style file processing.

The central question BufferedReader answers is this: how can a Java program read lines of text from a file quickly and conveniently, without wasting time on one-character-at-a-time disk access? Understanding BufferedReader is your first step toward handling real-world data files in Java.

SECTION 2

Core Principles & Definitions

Before diving into code, you need to understand a few foundational ideas that make BufferedReader work. These concepts are central to all file I/O in Java, so mastering them here will serve you well as you tackle more advanced topics later.

1

Stream

A stream is a sequence of data flowing from a source (like a file) to your program, or from your program to a destination. Think of it as a pipeline that delivers data one piece at a time.
2

Reader

A Reader is Java's abstract class for reading character-based (text) data. FileReader is a concrete Reader that connects to a file on disk.
3

Buffer

A buffer is a temporary storage area in memory. BufferedReader loads a chunk of characters from the file into this buffer, so future reads come from fast RAM instead of the slow disk.
4

Wrapping Pattern

BufferedReader doesn't open a file directly — it wraps another Reader (like FileReader). This layered design is called the Decorator Pattern and adds buffering without changing the original Reader.
5

Resource Management

Every opened file consumes system resources. You must close the reader when you're done. Java's try-with-resources statement handles this automatically, preventing resource leaks.
✦

KEY TAKEAWAY

Imagine you're reading a textbook in a library. Without a buffer, you'd walk to the shelf, read one word, walk back to your desk, write it down, and repeat — incredibly slow. With a buffer, you photocopy an entire page, bring it to your desk, and read from the copy. You only walk to the shelf when you need a new page. That's exactly what BufferedReader does: it grabs a big chunk of the file at once and serves it from memory, making your program dramatically faster.

SECTION 3

How BufferedReader Works — Visual Explanation

The diagram below shows the data flow when your Java program reads a text file using BufferedReader. Notice how the buffer sits between the file on disk and your program, acting as a middleman that minimizes slow disk operations.

Data Flow: File → FileReader → BufferedReader → Your ProgramTEXT FILETEXT FILEHello WorldJava is funLine threeLine fourLine fiveLine sixLine sevenLine eight(on disk — slow)FileReaderReads charsone at a timefrom the file(unbuffered)bytesBufferedReaderHello WorldJava is funLine threeLine four...8192 chars defaultbulkYourProgramCallsreadLine()Gets one lineline💡 Only one disk access fills the buffer. Your program reads many lines from fast memory.
The text file lives on the slow disk (left). FileReader connects to it. BufferedReader loads a large chunk (default 8,192 characters) into its in-memory buffer. Your program calls readLine() and gets data from the fast buffer instead of the slow disk.

As the diagram shows, the key insight is that disk access is the bottleneck. By loading 8,192 characters at once into the buffer, BufferedReader converts hundreds of slow disk reads into a single fast one. When your program calls readLine(), it reads from the buffer in RAM. Only when the buffer runs out does BufferedReader go back to the disk for another chunk. This is why programs that use BufferedReader can be 10 to 100 times faster than programs that read one character at a time.

SECTION 4

How It Works — Code Anatomy

Now let's look at the actual Java code structure for reading a file with BufferedReader. There are three essential phases: opening the file, reading the content, and closing the resource. Let's examine each one.

Phase 1 — Opening the File

You create a BufferedReader by wrapping it around a FileReader object. The FileReader takes a file path (as a String or File object) and opens a connection to that file on disk. BufferedReader then adds the buffering layer on top.

CONSTRUCTOR PATTERN

BufferedReaderbr=newBufferedReader(newFileReader("data.txt"));BufferedReader br = new BufferedReader(new FileReader("data.txt"));

FileReader("data.txt") opens the file. BufferedReader(...) wraps the FileReader, adding an 8,192-character buffer.

Phase 2 — Reading Lines

The most commonly used method is readLine(), which returns the next line of text as a String. When there are no more lines to read, it returns null. This makes it perfect for a while loop that keeps reading until the file is exhausted.

READING LOOP PATTERN

Stringline;while((line=br.readLine())!=null)System.out.println(line);String line; while ((line = br.readLine()) != null) { System.out.println(line); }

Each call to readLine() assigns the next line to line. The loop exits when readLine() returns null (end of file).

Phase 3 — Closing the Resource

You must close the BufferedReader when you're done. The modern way is the try-with-resources statement, which automatically closes the reader even if an exception occurs. This replaces the old-fashioned pattern of manually calling br.close() in a finally block.

TRY-WITH-RESOURCES PATTERN

try(BufferedReaderbr=newBufferedReader(newFileReader("data.txt")))Stringline;while((line=br.readLine())!=null)System.out.println(line);catch(IOExceptione)e.printStackTrace();try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) { String line; while ((line = br.readLine()) != null) { System.out.println(line); } } catch (IOException e) { e.printStackTrace(); }

The reader is declared inside try (...) parentheses. Java guarantees it will be closed when the block exits — even if an IOException is thrown.

💡

Don't Forget Imports!

You need to import two classes at the top of your file: import java.io.BufferedReader; and import java.io.FileReader;. You'll also need import java.io.IOException; to handle potential errors.

SECTION 5

Key Methods of BufferedReader

While readLine() is the most commonly used method, BufferedReader offers several other useful methods. The table below summarizes the ones you'll encounter most often when working with text files.

MethodReturn TypeDescription
readLine()StringReads a full line of text, excluding the newline character. Returns null at end of file.
read()intReads a single character as an integer (its Unicode value). Returns −1 at end of file.
ready()booleanReturns true if the reader has data ready to be read without blocking.
close()voidCloses the reader and releases the underlying file resource. Called automatically by try-with-resources.
lines()Stream<String>Returns a Stream of lines for functional-style processing (Java 8+). Useful for filtering, mapping, and collecting lines.

Commonly used BufferedReader methods

readLine() Loop — Step by StepSTARTCreate BufferedReaderreadLine()!= null ?YES ↓Process Lineprintln(line)loop backNO (null)END OF FILEReader auto-closesWhat happens inside:1. readLine() checks the buffer2. If data in buffer → return line3. If buffer empty → refill from disk4. If disk has no more data → null⚡ Most reads hit the buffer (fast!)
Flowchart of the standard readLine() loop pattern. The diamond decision box checks if the returned value is null. If not, the line is processed and the loop repeats. If null, the file is fully read and the reader closes.

The flowchart above illustrates the most important pattern you'll use with BufferedReader. Notice the while loop with the assignment inside the condition. The expression (line = br.readLine()) != null does two things at once: it reads a line and checks if it's null. This compact idiom is standard in Java file reading.

SECTION 6

Worked Example — Reading a Student Grades File

Suppose you have a text file called grades.txt that contains student names and their test scores, one per line. Let's write a complete Java program that reads this file, prints each line, and counts the total number of students.

ℹ

grades.txt Contents

Alice 95 Bob 87 Charlie 92 Diana 78 Evan 88

Complete Program: ReadGrades.java

  1. 1

    Step 1 — Import Required Classes

    At the top of your file, import the three classes you need: BufferedReader, FileReader, and IOException. These are all in the java.io package.
    import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException;
  2. 2

    Step 2 — Set Up the Class and Main Method

    Create the class ReadGrades with a main method. Declare a counter variable count to track the number of students.
    public class ReadGrades { public static void main(String[] args) { int count = 0;
  3. 3

    Step 3 — Open the File with Try-With-Resources

    Use the try-with-resources statement to create a BufferedReader wrapping a FileReader. This ensures the file is automatically closed, even if something goes wrong.
    try (BufferedReader br = new BufferedReader(new FileReader("grades.txt"))) {
  4. 4

    Step 4 — Read Lines in a While Loop

    Inside the try block, declare a String line variable. Use a while loop that reads lines until readLine() returns null. Inside the loop, print the line and increment the counter.
    String line; while ((line = br.readLine()) != null) { System.out.println(line); count++; }
  5. 5

    Step 5 — Handle Exceptions and Print Results

    Catch the IOException (which occurs if the file doesn't exist or can't be read). After the try-catch, print the total count.
    } catch (IOException e) { System.out.println("Error reading file: " + e.getMessage()); } System.out.println("Total students: " + count);
ℹ

Expected Output

Alice 95 Bob 87 Charlie 92 Diana 78 Evan 88 Total students: 5

SECTION 7

BufferedReader vs. Alternatives

Java offers several ways to read files. Understanding when to use BufferedReader versus its alternatives will help you choose the right tool for each situation. The table below compares the most common options you'll encounter.

FeatureBufferedReaderScannerFileReader (alone)
SpeedFast — buffered I/OModerate — uses internal buffer but slower parsingSlow — one char at a time
Reads lines?Yes — readLine()Yes — nextLine()No — only read() (chars)
Parses numbers?No — you must parse manuallyYes — nextInt(), nextDouble()No
Best forLarge text files, log processing, data importUser input, small files with mixed data typesRarely used alone — usually wrapped by BufferedReader
Thread-safe?Yes — synchronized methodsNoYes

Comparison of Java file reading approaches

🔧

WHEN TO USE WHAT

Think of it this way: BufferedReader is like a high-speed highway for reading text — it moves data in bulk and is your best choice for files. Scanner is like a Swiss army knife — versatile and great for parsing different data types from keyboard input, but slower for large files. Use BufferedReader when speed and simplicity matter, and Scanner when you need built-in parsing of numbers and tokens.

SECTION 8

Connection to Advanced I/O

BufferedReader is your entry point into Java's file I/O system, but the language offers much more powerful tools as you advance. Understanding how BufferedReader connects to these advanced features will give you a clear learning path.

ConceptWhat You Know (BufferedReader)What's Next (Advanced)
Reading filesBufferedReader with FileReaderFiles.readAllLines() and Files.lines() (NIO.2 API)
Writing filesYou've learned input; output is nextBufferedWriter with FileWriter (mirror pattern)
Binary filesBufferedReader handles text (characters)BufferedInputStream handles binary data (bytes) — images, audio
Data formatsPlain text, line by lineJSON, XML, and CSV parsing libraries build on top of reader classes

From BufferedReader basics to advanced I/O

Java's NIO.2 (New I/O) API, introduced in Java 7, provides convenience methods like Files.readAllLines(Path) that can read an entire file into a List<String> in a single line of code. Internally, these methods still use buffered reading. Once you're comfortable with BufferedReader, exploring NIO.2 is a natural next step — you'll find that the concepts of streams, buffering, and resource management carry over directly.

📝

Looking Ahead

The Decorator Pattern you learned here (wrapping one object around another to add functionality) is used throughout Java I/O. Once you understand this pattern, you can combine readers, writers, input streams, and output streams like building blocks to handle any I/O task.

SECTION 9

Practice Problems

Question 1: CONCEPTUAL

Why is it more efficient to use BufferedReader instead of FileReader alone when reading a text file? Explain the role of the buffer in your answer.
Show answer
FileReader reads one character at a time directly from the disk, which is slow because each read requires a separate disk access. BufferedReader wraps FileReader and loads a large chunk of data (8,192 characters by default) into an in-memory buffer all at once. Subsequent readLine() calls pull data from this fast in-memory buffer instead of the disk. This reduces the number of expensive disk operations from potentially thousands down to just a few, making file reading dramatically faster.

Question 2: BASIC CALCULATION

A text file contains exactly 50 lines, each with 100 characters. The BufferedReader default buffer size is 8,192 characters. Approximately how many times will BufferedReader need to access the disk to read the entire file?
Show answer
The total characters in the file are approximately 50 × 100 = 5,000 characters (plus newline characters, so roughly 5,050). The buffer holds 8,192 characters. Since 5,050 < 8,192, the entire file fits in a single buffer fill. BufferedReader would only need to access the disk once to read the whole file. Compare this to FileReader alone, which would make about 5,050 separate disk reads — one per character.

Question 3: INTERMEDIATE

Write a code snippet using BufferedReader and try-with-resources that reads a file called names.txt and prints only the lines that start with the letter 'A'. Include all necessary imports.
Show answer
The solution requires importing java.io.BufferedReader, java.io.FileReader, and java.io.IOException. Inside a try-with-resources block, create the BufferedReader and use the standard while loop. Inside the loop, add an if statement: if (line.startsWith("A")) { System.out.println(line); }. The startsWith() method is a String method that checks whether the line begins with the specified prefix. The catch block should handle IOException.

Question 4: APPLIED

You have a CSV file called temperatures.txt where each line contains a city name and a temperature separated by a comma (e.g., Denver,72). Write a program that uses BufferedReader to read this file and calculate the average temperature across all cities.
Show answer
First, declare variables double sum = 0; and int count = 0; before the try block. Inside the readLine loop, use line.split(",") to split each line into a String array. The temperature is at index 1: double temp = Double.parseDouble(parts[1]);. Add the temperature to sum and increment count. After the loop, compute the average as sum / count. This exercise combines BufferedReader with String manipulation and type conversion — skills you'll use constantly in real-world Java programming.

Question 5: CRITICAL THINKING

A student writes the following code, but it sometimes crashes with a NullPointerException. Identify the bug and explain how to fix it: BufferedReader br = new BufferedReader(new FileReader("data.txt")); String line = br.readLine(); while (line.length() > 0) { System.out.println(line); line = br.readLine(); } br.close();
Show answer
The bug is in the while loop condition: line.length() > 0. When readLine() reaches the end of the file, it returns null. Calling .length() on null causes a NullPointerException. The fix is to change the condition to while (line != null). Additionally, the code has a secondary issue: empty lines (length 0) would stop the loop prematurely, skipping the rest of the file. The original condition also fails to use try-with-resources, so if an exception occurs before br.close(), the file handle leaks. The corrected version should use the standard while ((line = br.readLine()) != null) pattern inside a try-with-resources block.

SUMMARY

Lesson Summary

Summary

BufferedReader is Java's go-to class for efficiently reading text files. It works by wrapping a FileReader using the Decorator Pattern and adding an in-memory buffer (default 8,192 characters) that dramatically reduces slow disk-access operations. The core reading loop uses readLine() in a while loop, continuing until the method returns null to signal the end of the file.

Always use try-with-resources to ensure the reader is properly closed and system resources are released. Compared to Scanner, BufferedReader is faster for reading files but doesn't parse numbers automatically. Remember to import java.io.BufferedReader, java.io.FileReader, and java.io.IOException. This foundation prepares you for advanced I/O topics including BufferedWriter, binary streams, and Java's NIO.2 API.

Varsity Tutors • Java • BufferedReader for Files — Read text files using BufferedReader (intro)