DEV Community

Cover image for FileReader ,BufferedReader ,FileWriter,BufferedWriter
Abishek
Abishek

Posted on

FileReader ,BufferedReader ,FileWriter,BufferedWriter

Class Hierarchy (With Meaning, Not Just Structure)

Reader (abstract)
   ↓
FileReader        → connects to file (source)
BufferedReader    → optimizes reading

Writer (abstract)
   ↓
FileWriter        → connects to file (destination)
BufferedWriter    → optimizes writing
Enter fullscreen mode Exit fullscreen mode

Key Insight

  • FileReader / FileWriter → act as a bridge between Java and the file system
  • BufferedReader / BufferedWriter → act as a performance layer on top of that bridge

This separation is intentional and follows good design principles.


FileReader — What Actually Happens Internally

FileReader fr = new FileReader("file.txt");
int ch = fr.read();
Enter fullscreen mode Exit fullscreen mode

Internal Flow

  1. JVM requests data from the OS
  2. OS reads data from disk into a kernel buffer
  3. JVM copies that data into a user-space buffer
  4. read() returns one character

The Problem

Even if the OS reads data in chunks internally, your code consumes it one character at a time.

So this loop:

while ((ch = fr.read()) != -1)
Enter fullscreen mode Exit fullscreen mode

results in:

  • repeated method calls
  • continuous boundary checks
  • inefficient data consumption

Bottleneck:
Frequent method calls combined with poor utilization of already-fetched data.


BufferedReader — What Changes?

BufferedReader br = new BufferedReader(new FileReader("file.txt"));
Enter fullscreen mode Exit fullscreen mode

Internal Behavior

  • Allocates an internal char[] buffer (typically ~8KB)
  • Calls FileReader.read(char[]) to fetch large chunks at once
  • Stores data in memory
  • Your code reads from this buffer instead of hitting the disk repeatedly

Deep Dive: readLine()

String line = br.readLine();
Enter fullscreen mode Exit fullscreen mode

This is not a simple operation.

Internally:

  1. Scans the buffer for \n (newline character)
  2. Constructs a String from characters up to that point
  3. If newline is not found:
  • refills the buffer
  • continues scanning
    1. Returns the completed string

Key Shift

Instead of:

  • multiple disk interactions

You now have:

  • one disk read
  • many in-memory operations

Why BufferedReader Is Faster (Actual Reasons)

Not just “it uses a buffer.”

1. Reduced System Calls

Each disk access involves a kernel transition, which is expensive.
BufferedReader minimizes how often this happens.

2. Lower CPU Overhead

Fewer method calls compared to character-by-character reading.

3. Better Memory Access Patterns

Sequential access to arrays improves CPU cache utilization.


FileWriter — Internal Behavior

FileWriter fw = new FileWriter("file.txt");
fw.write("Hello");
Enter fullscreen mode Exit fullscreen mode

What Happens

  • Data moves from Java → JVM buffer
  • JVM passes it to the OS
  • OS writes it to disk

Problem

Each write() can trigger disk interaction depending on buffering behavior.

Impact:
Frequent writes lead to frequent disk operations.


BufferedWriter — What Actually Improves?

BufferedWriter bw = new BufferedWriter(new FileWriter("file.txt"));
Enter fullscreen mode Exit fullscreen mode

Internal Flow

  • Data is written into an internal buffer
  • It does not immediately go to disk
  • When buffer fills or flush()/close() is called:

    • entire buffer is written in a single operation

Deep Detail: flush()

bw.flush();
Enter fullscreen mode Exit fullscreen mode

This forces:

JVM buffer → OS buffer → Disk
Enter fullscreen mode Exit fullscreen mode

Without calling flush():

  • data may remain in memory
  • file output may appear incomplete

Composition Design (Decorator Pattern)

BufferedReader br = new BufferedReader(new FileReader("file.txt"));
Enter fullscreen mode Exit fullscreen mode

This is an example of the Decorator Pattern.

  • FileReader → provides raw data access
  • BufferedReader → enhances behavior

Why This Design Exists

  • Separation of concerns
  • Reusable components
  • Flexible layering

You can mix and match behaviors without rewriting logic.


Why Not Just Use FileReader Everywhere?

Because it does not scale.

Problem Impact
Frequent reads High CPU and system call overhead
No batching Poor throughput
No abstraction Manual parsing required

Memory vs Performance Tradeoff

Buffered classes:

  • Use additional memory (internal buffers)
  • Significantly reduce disk I/O

This is a classic systems tradeoff:

Spend a small amount of RAM to gain large performance improvements.


Try-With-Resources — What It Actually Solves

try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
}
Enter fullscreen mode Exit fullscreen mode

What It Guarantees

  • close() is always called
  • For writers, this ensures flush() happens
  • OS-level file handles are released

Top comments (0)