Java BufferedReader
Table of Contents + β
In the last lesson you learned about Java writing files. Now letβs read text back, but faster than one character at a time. The tool for the job is BufferedReader. It lets you pull a whole line at once.
π€ The problem BufferedReader solves
Say you have a text file with a thousand lines and want to print each one. A basic reader has two problems:
- It gives you text one character at a time, so you glue letters into lines by hand. That is a lot of fussy code.
- Each one-character request is a slow disk trip. Doing that for every letter in a big file is painfully slow.
BufferedReader fixes both:
- It reads text in large chunks into a buffer, a small holding area in memory.
- Disk trips happen rarely and in big gulps, so reading is fast.
- Its
readLine()method returns a whole line at once, so you never glue characters together.
π§© How BufferedReader fits together
BufferedReader does not talk to the file directly. It wraps another reader and adds the buffer on top. For a file, that inner reader is usually FileReader.
Think of a tap and a bucket. FileReader is the tap connected to the file, giving water one drop at a time. BufferedReader is a bucket under the tap. It fills in one go, then you scoop as much as you like.
So the classic pattern is a FileReader wrapped in a BufferedReader:
BufferedReader reader = new BufferedReader(new FileReader("notes.txt"));- The inner
new FileReader("notes.txt")is the real connection to the file. - The outer
new BufferedReader(...)adds the speed buffer andreadLine(). - You almost always use them together like this.
π Reading a file line by line
Here is the pattern you will use most often in your whole Java life. Read each line with readLine() inside a while loop, and stop when it returns null. This program prints every line of a file with a line number in front.
import java.io.BufferedReader;import java.io.FileReader;import java.io.IOException;
public class Main { public static void main(String[] args) { try (BufferedReader reader = new BufferedReader(new FileReader("notes.txt"))) { String line; int number = 1; while ((line = reader.readLine()) != null) { // β
stop when readLine returns null System.out.println(number + ": " + line); number++; } } catch (IOException e) { System.out.println("Could not read the file: " + e.getMessage()); } }}The important parts:
new FileReader("notes.txt")opens the file as the inner reader.new BufferedReader(...)wraps it so we get the buffer andreadLine().reader.readLine()returns the next line as aString, without the line-break.- When there are no more lines,
readLine()returnsnull, which ends the loop. - The
try (...)is try-with-resources. It closes the reader when the block finishes.
The line while ((line = reader.readLine()) != null) reads a line into line and checks it for null in the same step. That is what makes this loop so short and so common. Get comfortable with this exact shape.
If notes.txt holds these three lines:
Buy milkCall AlexFinish reportthen the program prints the lines with numbers in front.
Output
1: Buy milk2: Call Alex3: Finish reportβ‘ Why buffering is faster
Picture the difference:
- Without a buffer, every read is a separate request with a fixed cost. A thousand one-character reads pay that cost a thousand times.
- With a buffer,
BufferedReadergrabs a big block at once and keeps it in memory. - Each
readLine()hands you text from that block, with no disk trip. - Only when the block runs out does it fetch another. The slow trips become rare.
The first version reads one character at a time straight from a FileReader, which is slow on a big file.
import java.io.FileReader;import java.io.IOException;
public class Main { public static void main(String[] args) { try (FileReader reader = new FileReader("big.txt")) { int c; while ((c = reader.read()) != -1) { // β one slow character at a time System.out.print((char) c); } } catch (IOException e) { System.out.println("Read failed: " + e.getMessage()); } }}The version below wraps the same FileReader and reads whole lines. Same result, much faster, far easier to read.
import java.io.BufferedReader;import java.io.FileReader;import java.io.IOException;
public class Main { public static void main(String[] args) { try (BufferedReader reader = new BufferedReader(new FileReader("big.txt"))) { String line; while ((line = reader.readLine()) != null) { // β
a whole line at a time, buffered System.out.println(line); } } catch (IOException e) { System.out.println("Read failed: " + e.getMessage()); } }}The rule of thumb: Always wrap a FileReader in a BufferedReader. You get speed and you get readLine(). There is almost no reason to use a bare FileReader in a loop.
β¨οΈ Reading from the keyboard with System.in
BufferedReader is not only for files. It can also read what a person types in the console:
- Keyboard input lives in
System.in, a stream of raw bytes, not text. - Wrap it in an
InputStreamReaderto turn those bytes into characters. - Wrap that in a
BufferedReaderforreadLine().
This program asks for a name and a city, then prints a greeting.
import java.io.BufferedReader;import java.io.InputStreamReader;import java.io.IOException;
public class Main { public static void main(String[] args) { try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) { System.out.print("Your name: "); String name = reader.readLine(); // β
reads one typed line System.out.print("Your city: "); String city = reader.readLine(); System.out.println("Hello " + name + " from " + city + "!"); } catch (IOException e) { System.out.println("Could not read input: " + e.getMessage()); } }}The wrapping runs from the inside out: System.in (raw bytes) β InputStreamReader (characters) β BufferedReader (buffer plus readLine()).
If the person types Sam and then Berlin, the run looks like this.
Output
Your name: SamYour city: BerlinHello Sam from Berlin!One note on closing:
- Closing the
BufferedReaderalso closesSystem.in, which you usually cannot reopen. - In a short program that reads input once and ends, try-with-resources is fine.
- If your program keeps reading input later, leave this reader open for the life of the program.
π Reading all lines at once
Sometimes you do not want a loop. You just want every line in a list so you can sort it, count it, or pass it around.
The lines() method on a BufferedReader gives you a stream of the lines, a flow of values you process one after another. This example collects them into a list and prints how many there are.
import java.io.BufferedReader;import java.io.FileReader;import java.io.IOException;import java.util.List;import java.util.stream.Collectors;
public class Main { public static void main(String[] args) { try (BufferedReader reader = new BufferedReader(new FileReader("notes.txt"))) { List<String> lines = reader.lines() .collect(Collectors.toList()); // β
all lines in a list System.out.println("Total lines: " + lines.size()); System.out.println("First line: " + lines.get(0)); } catch (IOException e) { System.out.println("Could not read: " + e.getMessage()); } }}Breaking that down:
reader.lines()produces a stream where each item is one line..collect(Collectors.toList())gathers every line into aListofString.- Then you use the list like any other, here checking its size and first item.
For the three-line notes.txt from before, the output is short.
Output
Total lines: 3First line: Buy milkAn even shorter helper is Files.readAllLines(path). It hands back a List of lines in one call and handles opening and closing. Use BufferedReader to read line by line in a loop, and Files.readAllLines when you just want the whole file as a list.
π Handling IOException
Reading can fail, so the methods throw a checked exception called IOException:
- The file might not exist, or the folder might be wrong.
- You might not have permission to open it.
- The compiler forces you to deal with it. You cannot pretend it never happens.
- Handle it by catching with
try/catch, or by declaringthrows IOException.
The examples above caught it and printed a message, the usual choice for a small program. This snippet shows the throws style instead.
import java.io.BufferedReader;import java.io.FileReader;import java.io.IOException;
public class Main { public static void main(String[] args) throws IOException { // β
passes the error on try (BufferedReader reader = new BufferedReader(new FileReader("notes.txt"))) { String line; while ((line = reader.readLine()) != null) { System.out.println(line); } } }}Catching lets you show a clear message and keep control. Declaring throws is simpler but lets the program stop on error. For real apps, catching and reporting is kinder to the person using your program.
Pick a real message
When you catch IOException, print something a person can act on, like which file failed. A vague message such as βerrorβ makes it hard to know what went wrong.
β οΈ Common Mistakes
A few BufferedReader slip-ups to watch for.
- Not closing the reader. An open reader holds onto the file and wastes resources. Use try-with-resources so it always closes.
BufferedReader r = new BufferedReader(new FileReader("notes.txt"));String line = r.readLine(); // β never closed, file stays open
try (BufferedReader r = new BufferedReader(new FileReader("notes.txt"))) { String line = r.readLine(); // β
closed automatically}- Forgetting the null check, so the loop never ends. If you do not stop on
null, the loop runs forever or breaks badly.
String line;while (true) { // β no null check, never stops cleanly line = reader.readLine(); System.out.println(line);}
while ((line = reader.readLine()) != null) { // β
stops when the file ends System.out.println(line);}- Reading character by character without a buffer. A bare
FileReaderin a loop is slow and clumsy. Wrap it.
FileReader r = new FileReader("big.txt");int c;while ((c = r.read()) != -1) { } // β slow, one character at a time
BufferedReader r2 = new BufferedReader(new FileReader("big.txt"));String line;while ((line = r2.readLine()) != null) { } // β
fast, a whole line at a timeβ Best Practices
Habits that make reading text safe and clean.
- Always use try-with-resources. Open the reader inside
try (...)so Java closes it for you, even when an error happens. This is the single most important habit. - Wrap FileReader in BufferedReader. You get speed from the buffer and the handy
readLine()method. There is almost no reason to loop over a bareFileReader. - Use the readLine-until-null loop. The
while ((line = reader.readLine()) != null)shape reads and checks in one step and is the standard way to walk a file. - Always handle IOException. Catch it and print a clear, specific message, or declare
throwsif a caller will handle it. - For keyboard input, chain System.in. Use
new BufferedReader(new InputStreamReader(System.in))to read typed lines withreadLine().
π§© What Youβve Learned
Nicely done. Letβs recap BufferedReader.
- β
BufferedReaderwraps another reader likeFileReaderand reads text efficiently through a buffer. - β
The classic pattern is
new BufferedReader(new FileReader(path))thenreadLine()in a while loop untilnull. - β Buffering is faster because it reads big chunks instead of one slow character at a time.
- β
For keyboard input, use
new BufferedReader(new InputStreamReader(System.in)). - β
lines()gives a stream of lines, andFiles.readAllLinesgives a list in one call. - β
Always use try-with-resources to close the reader and always handle
IOException.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What does BufferedReader.readLine() return when there are no more lines?
Why: readLine() returns null at the end of the input, which is how the while loop knows to stop.
- 2
What is the classic way to create a BufferedReader for a file?
Why: You wrap a FileReader in a BufferedReader to add the buffer and the readLine() method.
- 3
Why is BufferedReader faster than reading character by character?
Why: Buffering grabs large blocks of text at once, so the slow disk requests happen rarely.
- 4
How do you read typed keyboard input with BufferedReader?
Why: Wrap System.in in an InputStreamReader to get characters, then in a BufferedReader for readLine().
π Whatβs Next?
You can now read text quickly, line by line, from files and from the keyboard. The natural partner of an efficient reader is an efficient writer. Next you will meet the writing side of the same idea, which buffers your output the same way for speed.