Java I/O Class Hierarchy
All the stream classes in java.io grow from four abstract base classes: InputStream and OutputStream for bytes, and Reader and Writer for characters. Each base class defines the basic methods, such as read, write and close, and each subclass connects them to a particular source or adds a feature, such as buffering. Once you know the four base classes, every other stream class is easy to place. In this lesson, you will learn the hierarchy and the methods of each base class.
The Hierarchy
Methods of InputStream
| Method | Description |
|---|---|
int read() | It is used to read one byte (0 to 255), or -1 at the end. |
int read(byte[] b) | It reads up to b.length bytes into the array and returns how many were read. |
byte[] readAllBytes() | It reads everything that's left (Java 9). |
long transferTo(OutputStream out) | It copies all remaining bytes to an output stream (Java 9). |
int available() | It estimates how many bytes can be read without waiting. |
void close() | It closes the stream and releases its resources. |
Methods of OutputStream
| Method | Description |
|---|---|
void write(int b) | It writes one byte (the lowest 8 bits of b). |
void write(byte[] b) | It writes a whole array. |
void write(byte[] b, int off, int len) | It writes part of an array. |
void flush() | It pushes any buffered bytes to the destination. |
void close() | It flushes and closes the stream. |
Methods of Reader and Writer
Reader has the same kind of methods as InputStream, but with characters: read() returns one char (as an int), read(char[] buf) fills a char array, and transferTo(Writer) copies everything. Writer mirrors OutputStream and adds write(String s) and append(CharSequence).
Example: Programming to the Base Class
Because all streams share these methods, a method can accept any InputStream without caring where the bytes come from:
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class AnySource {
static int countBytes(InputStream in) throws IOException {
return in.readAllBytes().length;
}
public static void main(String[] args) throws IOException {
try (FileOutputStream out = new FileOutputStream("notes.txt")) {
out.write("Navika Anamaneni".getBytes());
}
try (InputStream file = new FileInputStream("notes.txt");
InputStream memory = new ByteArrayInputStream(new byte[100])) {
System.out.println("File: " + countBytes(file) + " bytes");
System.out.println("Memory: " + countBytes(memory) + " bytes");
}
}
}File: 16 bytes
Memory: 100 bytesExplanation: countBytes only knows about InputStream. It works the same with a file stream and with an in-memory stream, because both extend InputStream. Two resources in one try-with-resources statement are closed in reverse order.
Common Mistakes
Calling read() in a loop on a large file without buffering. Each call may go to the disk; wrap the stream in a buffered stream.
Relying on available() for the file size. It's only an estimate; read to the end or use Files.size.
Forgetting flush() when a stream must be sent before closing. Buffered data stays in memory until flushed or closed.
Practice
- Easy: Write a method
copy(InputStream in, OutputStream out)withtransferTo, and use it to copy one file to another. - Harder: Write a method that reads any
Readerand returns the number of lines, and test it with aFileReaderand aStringReader.
Key Takeaways
Was this tutorial helpful?