Unravel › Java concepts › I/O and files
Byte streams, readers and writers
Java has two families for moving data: byte streams (InputStream, OutputStream) for raw bytes, and character readers and writers (Reader, Writer) that decode bytes to text with a charset.
Learn these first
At a glance
Plumbing: the raw pipe carries water (bytes).
- 1InputStreamReader(in, UTF_8) turns a byte stream into characters; BufferedReader adds readLine()OutputStreamWriter, BufferedWriter and PrintWriter do the reverse for writing.
- 2Wrapping is called decoration: new BufferedInputStream(new FileInputStream(f))Close only the outermost wrapper; it closes the inner ones.
- 3ByteArrayInputStream / ByteArrayOutputStream and StringReader / StringWriter fake a file in memory, which is perfect for tests
- 4in.readAllBytes() reads everything; in.transferTo(out) copies a stream into another without writing a loop; DataOutputStream writes ints, longs and strings in a binary format
- Unbuffered byte-by-byte reading from a file is extremely slow (one system call per byte)
- PrintWriter and PrintStream never throw IOException; they set an error flag (checkError())
- A BufferedReader fills an internal char[] (8192 by default) in large reads and serves readLine from it
import java.io.*;import java.nio.charset.StandardCharsets;public class Copy { static byte[] gather(InputStream in) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); in.transferTo(out); return out.toByteArray(); } static void write(File f, String text) throws IOException { try (Writer w = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f), StandardCharsets.UTF_8))) { w.write(text); } } static String fromMemory(byte[] data) throws IOException { try (BufferedReader r = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(data), StandardCharsets.UTF_8))) { return r.readLine(); } }}- line 5 uses ByteArrayOutputStream, the core of Byte streams, readers and writers
- line 6 uses transferTo, the core of Byte streams, readers and writers
- line 10 uses Writer, the core of Byte streams, readers and writers
- line 13 uses Reader, the core of Byte streams, readers and writers
Read the full explanation (about 1 min)
Java has two families for moving data: byte streams (InputStream, OutputStream) for raw bytes, and character readers and writers (Reader, Writer) that decode bytes to text with a charset. Wrapping one in another adds buffering and conversion.
A mental model
Plumbing: the raw pipe carries water (bytes). A reader is a filter that turns the water into drinking glasses of text, and a buffer is a small tank that smooths the flow.
How it works
InputStreamReader(in, UTF_8) turns a byte stream into characters; BufferedReader adds readLine(). OutputStreamWriter, BufferedWriter and PrintWriter do the reverse for writing.
Wrapping is called decoration: new BufferedInputStream(new FileInputStream(f)). Close only the outermost wrapper; it closes the inner ones.
ByteArrayInputStream / ByteArrayOutputStream and StringReader / StringWriter fake a file in memory, which is perfect for tests.
in.readAllBytes() reads everything; in.transferTo(out) copies a stream into another without writing a loop; DataOutputStream writes ints, longs and strings in a binary format.
Going deeper
Unbuffered byte-by-byte reading from a file is extremely slow (one system call per byte). Always buffer, or read into a byte[] of 8 KB or more.
PrintWriter and PrintStream never throw IOException; they set an error flag (checkError()). If you need to know that a write failed, use a BufferedWriter.
Flush before the data must be visible, and close in a finally or try-with-resources, or buffered data is lost. System.in and System.out should not be closed.
Under the hood
A BufferedReader fills an internal char[] (8192 by default) in large reads and serves readLine from it. The decorator design means a new source (a socket, a zip entry) works with all the existing readers and filters.
Common mistakes
- Reading from a file one byte at a time without buffering.
- Using
FileReaderand relying on the platform charset. - Not closing the stream (file handle leak).
How Unravel recognises it
When you paste code, Unravel looks for these signs of byte streams, readers and writers. Spotting them yourself is a good habit when you read unfamiliar code:
- Types:
ByteArrayInputStreamByteArrayOutputStreamDataInputStreamDataOutputStreamPrintWriterPrintStreamStringReaderStringWriterBufferedInputStreamBufferedOutputStreamOutputStreamWriterRandomAccessFileReaderWriter - Method calls:
readAllBytes()transferTo()readNBytes()
Part of a learning path
Related concepts
Builds on this
Frequently asked questions
What is Byte streams, readers and writers?
Java has two families for moving data: byte streams (InputStream, OutputStream) for raw bytes, and character readers and writers (Reader, Writer) that decode bytes to text with a charset. Wrapping one in another adds buffering and conversion.
What are the most common mistakes with Byte streams, readers and writers?
Reading from a file one byte at a time without buffering. Using FileReader and relying on the platform charset. Not closing the stream (file handle leak).
How can I see Byte streams, readers and writers in real code?
Paste your own Java into Unravel and it will point out where byte streams, readers and writers appears, explain how it works at the detail level you choose, and draw the flow of the code around it.