Byte streams vs character streams
InputStream/OutputStream for raw bytes, Reader/Writer for text — and why the distinction matters.
Two families of streams
Java's classic I/O layer splits into two parallel hierarchies based on what you're moving:
Byte streams (InputStream / OutputStream) move raw binary data — eight bits at a time. Use them for images, audio, serialised objects, or any file where you cannot assume a text encoding.
Character streams (Reader / Writer) move decoded text — they handle charset conversion so your code sees char values rather than raw bytes. Use them whenever you are reading or writing text.
The bridge: InputStreamReader wraps an InputStream and decodes bytes to chars using a specified charset:
Reader reader = new InputStreamReader(System.in, StandardCharsets.UTF_8);
Buffering is almost always the right default — raw streams do one system call per read/write, which is slow. Wrap with a Buffered decorator:
BufferedReader br = new BufferedReader(new FileReader("notes.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("out.txt"));
The three standard streams:
| Stream | Type | Purpose |
|---|---|---|
| System.in | InputStream | keyboard / stdin |
| System.out | PrintStream | normal output / stdout |
| System.err | PrintStream | error output / stderr |
PrintStream (which System.out and System.err use) is a convenience byte stream that adds print/println/printf — it is not a Writer despite printing text.