Byte Streams vs Character Streams
Fast recall
Quick revision
Core answer
InputStream and OutputStream) for raw binary data and character streams (Reader and Writer) for text. Character streams decode and encode bytes with a charset; choosing the wrong kind can corrupt text or binary content.Interview-ready guide
Interview answer
Java has two main stream families. InputStream and OutputStream move raw bytes, so they are used when every byte must stay unchanged, such as for a PNG image, ZIP archive, encrypted message, or binary protocol. Reader and Writer work with text characters.
Text is stored and sent as bytes, so a character stream needs a charset such as UTF-8. InputStreamReader decodes bytes into characters, and OutputStreamWriter encodes characters back into bytes. Buffering is a separate feature: BufferedReader buffers decoded text, while BufferedInputStream buffers bytes without decoding them.
For example, a UTF-8 CSV can be read with Files.newBufferedReader(path, UTF_8) and processed line by line. An uploaded JPEG should stay in byte streams and can be copied with input.transferTo(output). Passing the JPEG through a Reader may change byte sequences and corrupt the file.
Choose by the meaning of the data, not only by its extension: binary payloads use byte streams; text uses character streams with the correct charset. A Reader returns UTF-16 code units, so one read() is not always a full visible character. It solves byte decoding, but code that handles complex Unicode may still need to work with code points or grapheme clusters.
Byte Streams vs Character Streams
Direct Answer
Java has byte streams (InputStream and OutputStream) for raw binary data and character streams (Reader and Writer) for text. Character streams decode and encode bytes with a charset; choosing the wrong kind can corrupt text or binary content.
Deep Dive & Architecture
Bytes have no built-in language or character meaning. A decoder interprets byte sequences according to a charset, and an encoder performs the reverse mapping. If producer and consumer disagree, characters can become replacement symbols or mojibake even though every I/O call succeeds.
A bridge can read ahead because a character may require several bytes. That is why byte-level and character-level reads should not be mixed casually on the same underlying stream: their logical positions may no longer match.
Binary data has no valid general-purpose character decoding. Base64 is an explicit binary-to-text representation, not a reason to pass arbitrary binary bytes through a Reader.
Code Example
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
class ByteAndCharacterStreams {
public static void main(String[] args) throws Exception {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
try (var writer = new OutputStreamWriter(bytes, StandardCharsets.UTF_8)) {
writer.write("café");
}
try (var reader = new BufferedReader(new InputStreamReader(
new ByteArrayInputStream(bytes.toByteArray()), StandardCharsets.UTF_8))) {
System.out.println(reader.readLine());
}
}
}
Check your understanding
Answer these without looking back at the article.
- What does InputStreamReader do?
- Why can a Reader corrupt a binary file?
- Does one Reader.read call always return one visible character?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.