Byte Streams vs Character Streams
Byte Streams
Operate on raw 8-bit bytes. Ideal for binary data such as images, audio, video, or archives—any content where character encoding is irrelevant.
import java.io.FileInputStream;
public class ByteStreamExample {
public static void main(String[] args) throws Exception {
try (FileInputStream input = new FileInputStream("data.bin")) {
int byteValue;
while ((byteValue = input.read()) != -1) {
System.out.print((char) byteValue); // May produce garbled text for non-ASCII content
}
}
}
}
read()fetches one byte at a time.- Returns
-1at end-of-file. - No encoding handling—text may appear corrupted if interpreted as characters.
Character Streams
Work with 16-bit Unicode characters and handle encoding (e.g., UTF-8) transparently. Best suited for readable text files like .txt, .java, or .md.
import java.io.FileReader;
public class CharStreamExample {
public static void main(String[] args) throws Exception {
try (FileReader reader = new FileReader("document.txt")) {
int ch;
while ((ch = reader.read()) != -1) {
System.out.print((char) ch);
}
}
}
}
- Automatically decodes bytes into characters using the platform’s default charset (or a specified one).
- Avoids manual encoding management for textual content.
Stream Usage Pattern
- Open a stream.
- Perform read/write operations.
- Process the data.
- Explicitly close the stream—resources aren’t released automatically by garbage collection.
The try-with-resources statement ensures automatic closure:
try (FileInputStream stream = new FileInputStream("input.dat")) {
// Use stream
} // Automatically closed here
Buffered Character Streams for Efficiency
For better performance with text, wrap character streams in buffered versions:
import java.io.*;
public class BufferedTextIO {
public static void main(String[] args) throws Exception {
// Reading line by line
try (BufferedReader reader = new BufferedReader(new FileReader("input.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
// Writing with buffering
try (BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) {
writer.write("Hello Java");
writer.newLine();
writer.write("Written via character stream");
}
}
}
BufferedReader and BufferedWriter use internal buffers to reduce system calls, significantly improving I/O efficiency.
Design Insight: Decorator vs Task Wrapper
BufferedReader follows the decorator pattern—it wraps another Reader to add buffering capability while remaining a Reader itself:
public class BufferedReader extends Reader {
protected Reader in; // Delegates to wrapped reader
}
In contrast, FutureTask serves a different purpose: it adapts a Callable into a RunnableFuture, managing execution state and result retrieval—a semantic transformation, not just feature enhancement.