Buffered Streams
By default, standard streams like FileInputStream and FileWriter execute I/O operations directly through the operating system. This means every read or write request is handled directly by the disk or network, which is extremely inefficient and slow.
To resolve this performance bottleneck, Java provides Buffered Streams. These streams use an in-memory buffer (typically 8KB) to store data temporarily, minimizing the number of direct system calls.
How Buffering Works
Instead of requesting data from the disk byte-by-byte:
- Reading: The buffered stream reads a large block of data from the disk at once and stores it in an internal buffer. Subsequent read operations pull directly from this memory buffer. Once the buffer is empty, another block is fetched.
- Writing: The buffered stream accumulates written data in its memory buffer. Once the buffer is full (or when the stream is flushed/closed), the entire chunk is written to the disk in a single operating system operation.
Buffered Streams Classes
Java wraps existing streams to add buffering capabilities using the decorator design pattern.
| Stream Type | Unbuffered Class | Buffered Class Wrapper |
|---|---|---|
| Byte Input | InputStream | BufferedInputStream |
| Byte Output | OutputStream | BufferedOutputStream |
| Char Input | Reader | BufferedReader |
| Char Output | Writer | BufferedWriter |
Working with Buffered Streams
Wrapping Streams
You wrap an unbuffered stream by passing it to the constructor of the buffered stream:
// Wrapping a Character Stream
BufferedReader reader = new BufferedReader(new FileReader("input.txt"));
BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"));
// Wrapping a Byte Stream
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("input.bin"));
[!NOTE] When you close a wrapper/decorator stream (like
BufferedReader), it automatically calls theclose()method of the underlying stream (FileReader). You do not need to close both.
Useful Methods in BufferedReader & BufferedWriter
String readLine()(BufferedReader): Reads a full line of text. Returns the line as aString(excluding the line termination characters\nor\r\n), ornullif the end of the stream is reached.void newLine()(BufferedWriter): Writes a platform-specific line separator (e.g.\non Unix,\r\non Windows).void flush()(BufferedWriter): Forces any buffered data to be written immediately to the target.
Practical Code Example: Line-by-Line File Copy
Here is a complete program demonstrating how to read a text file line-by-line using BufferedReader and write it to another file using BufferedWriter.
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedCopyDemo {
public static void main(String[] args) {
String sourceFile = "large_input.txt";
String destFile = "large_output.txt";
// Create temporary sample file for the demo
try (FileWriter setupWriter = new FileWriter(sourceFile)) {
setupWriter.write("Line 1: Welcome to Java Buffered Streams.\n");
setupWriter.write("Line 2: Buffering is highly optimized for performance.\n");
setupWriter.write("Line 3: End of the file.");
} catch (IOException e) {
e.printStackTrace();
}
// Run the buffered copy operation
try (BufferedReader reader = new BufferedReader(new FileReader(sourceFile));
BufferedWriter writer = new BufferedWriter(new FileWriter(destFile))) {
String line;
int lineCount = 0;
// readLine() reads until a newline character or end of stream
while ((line = reader.readLine()) != null) {
lineCount++;
// Write the line along with a platform-appropriate newline
writer.write("Processed " + line);
writer.newLine();
}
// Flush ensures all data is written to disk from the buffer
writer.flush();
System.out.println("Finished copying. Total lines copied: " + lineCount);
} catch (IOException e) {
System.err.println("I/O error during copying: " + e.getMessage());
e.printStackTrace();
}
}
}