Byte streams vs character streams in Java
InputStream/OutputStream vs Reader/Writer.
FileReader and FileWriter. The copy has the right size-ish… but won't open. The code looked fine. What did Java do to your pixels?Two families
Java I/O comes in two families. **InputStream/OutputStream move raw bytes — right for images, ZIPs and network data. Reader/Writer move characters — right for text. Handy rule: names ending in Stream handle bytes; names ending in Reader/Writer** handle chars.
The bridge: bytes → chars
Text arrives as bytes, so something must decode them using a charset. **InputStreamReader wraps an InputStream and hands out characters; OutputStreamWriter** does the reverse.
Reader r = new InputStreamReader(
System.in, StandardCharsets.UTF_8);
// System.in gives bytes, r gives charsBytes in memory
ByteArrayOutputStream collects bytes in memory. What prints?
var out = new ByteArrayOutputStream();
out.write(65);
out.write(66);
out.write(67);
System.out.println(out.size());
System.out.println(
out.toString(StandardCharsets.UTF_8));3 ABC6 6566673 656667
Show the answer
3 — write(int) stores one byte each. Decoding those bytes as UTF-8 gives the characters **ABC** (65, 66, 67).
Why read() returns an int
read() returns the next byte (0–255) or character code as an **int, and -1 at the end** of the stream. Using int leaves room for -1, which can never be confused with real data.
Reading to the end
What does this print?
void main() throws IOException {
Reader r = new StringReader("hi");
System.out.println(r.read());
System.out.println(r.read());
System.out.println(r.read());
}h i null104 105 -1104 105 0
Show the answer
104, 105 — the character codes of 'h' and 'i' — then **-1**: the end of the stream.
Reading an image as text
A Reader decodes bytes into characters. Image bytes aren't valid text, so invalid sequences get replaced and the copy is corrupted. For binary data use **FileInputStream/FileOutputStream — or simply Files.copy**.
// corrupts binary files:
new FileReader("photo.png");
// correct:
Files.copy(src, dst);Bytes vs text on the job
File uploads, PDFs, images and network protocols are bytes; CSVs, logs and JSON are text. Mixing them up causes corrupted downloads and mysterious "?" characters — a common bug when someone reads a binary file with a Reader "just to copy it".
Key takeaways
- InputStream/OutputStream: bytes (binary data)
- Reader/Writer: chars (text), decoded with a charset
- InputStreamReader turns bytes into chars
- read() returns an int; -1 means end of stream
System.out is a byte stream (PrintStream) under the hood: every println you've ever written encodes characters into bytes.
Practice questions
What does this print?
var out = new ByteArrayOutputStream();
out.write(72);
out.write(105);
System.out.println(out.size());
System.out.println(
out.toString(StandardCharsets.UTF_8));- 2 Hi
- 6 72105
- 2 72105
- 1 Hi
Check your answer
2 Hi. write(int) stores one byte each: 72 and 105. Decoding those 2 bytes as UTF-8 gives the characters 'H' and 'i'.
You need to copy a PNG image file. Which should you use?
- FileReader and FileWriter
- FileInputStream and FileOutputStream (or Files.copy)
- BufferedReader.readLine()
- Scanner
Check your answer
FileInputStream and FileOutputStream (or Files.copy). An image is binary. A Reader would try to decode the bytes as text, mangling any byte sequence that isn't valid in the charset.