Definition
Java I/O (Input/Output) is a set of APIs that enables reading and writing of data (files, streams, console, network, etc.). It supports both byte-based and character-based data.
Java NIO (New I/O) is a modern alternative introduced in Java 1.4 that supports buffer-oriented, non-blocking I/O with advanced features like channels, selectors, and memory-mapped files.
History and Java Version Milestones
Java Version | Feature/Improvement | Description |
Java 1.0 | java.io package | Basic stream classes: InputStream, OutputStream, Reader, Writer |
Java 1.1 | Serialization (Serializable) | Allows saving objects to a stream and reconstructing them later |
Java 1.4 | New I/O (NIO) - java.nio | Introduced Buffer, Channel, Selector for faster, non-blocking I/O |
Java 7 | AutoCloseable with try-with-resources | Simplified resource management using try-with-resources (corrected from Java 5) |
Java 7 | NIO.2 - java.nio.file | Added Path, Files, DirectoryStream, WatchService |
Java 8 | Stream APIs with I/O | I/O operations like Files.lines(Path) return Stream for functional processing |
Java 9 | Reactive Streams (Flow API) | Introduced java.util.concurrent.Flow for asynchronous processing |
Java 11+ | Minor updates | Enhancements to file APIs (Files.readString, Files.writeString, etc.) |
Types of Java I/O
Type | API | Use Case |
Byte Stream | InputStream / OutputStream | Raw binary data like images, audio |
Character Stream | Reader / Writer | Text data (supports Unicode) |
Data Stream | DataInputStream, DataOutputStream | Primitive data types (int, float, etc.) |
Buffered Stream | BufferedInputStream, BufferedReader | Improve performance with buffering |
Object Stream | ObjectInputStream, ObjectOutputStream | Serialization/deserialization |
NIO Buffer | ByteBuffer, CharBuffer, etc. | High-performance data manipulation |
NIO Channel | FileChannel, SocketChannel | File/network I/O |
NIO File API | Path, Files, WatchService | Modern file management |
Java I/O Classic Hierarchy (java.io)
java.io
| |── FileInputStream – Read bytes from file
| |── ByteArrayInputStream – Read bytes from a byte array
| |── ObjectInputStream – Deserialize objects
| |── BufferedInputStream – Adds buffering to InputStream
| |── PipedInputStream – Reads from a connected PipedOutputStream
|── OutputStream (abstract class, byte output) – Write bytes
| |── FileOutputStream – Write bytes to file
| |── ByteArrayOutputStream – Writes bytes into byte array
| |── ObjectOutputStream – Serializes objects
| |── BufferedOutputStream – Adds buffering
| |── PipedOutputStream – Writes to a connected PipedInputStream
| |── FileReader – Reads characters from file
| |── BufferedReader – Buffers characters for performance
| |── InputStreamReader – Converts InputStream to Reader
| |── StringReader – Reads from String
|── Writer (abstract class, character output) – Writes characters
| |── FileWriter – Writes characters to file
| |── BufferedWriter – Buffers characters for performance
| |── OutputStreamWriter – Converts OutputStream to Writer
| |── StringWriter – Writes to String buffer
|── File (class) – Represents file or directory pathnames
|── RandomAccessFile (class) – Allows file read/write at specific locations
|── Serializable (interface) – Enables object serialization
InputStream Hierarchy (Byte Input)
Class | One-liner Definition | Real-Time Use Case | Code Example |
InputStream | Base class for reading raw byte streams. | Superclass for all byte-based input streams. | — |
ByteArrayInputStream | Reads from a byte array as if it were a stream. | Read image/file loaded in memory. | new ByteArrayInputStream(byteArray) |
FileInputStream | Reads bytes from a file. | Load PDF/image file for processing. | new FileInputStream("file.pdf") |
BufferedInputStream | Buffers input for efficient reading. | Load large files faster. | new BufferedInputStream(new FileInputStream("log.txt")) |
DataInputStream | Reads Java primitives (int, float) from stream. | Reading binary file formats like .class files. | new DataInputStream(new FileInputStream("data.bin")) |
ObjectInputStream | Deserializes Java objects from a stream. | Read saved state of a Java object. | new ObjectInputStream(new FileInputStream("user.ser")) |
OutputStream Hierarchy (Byte Output)
Class | One-liner Definition | Real-Time Use Case | Code Example |
OutputStream | Base class for writing raw byte streams. | Superclass for all byte-based output streams. | — |
ByteArrayOutputStream | Writes to a byte array in memory. | Combine multiple byte arrays into one. | new ByteArrayOutputStream() |
FileOutputStream | Writes bytes to a file. | Save a downloaded image. | new FileOutputStream("image.jpg") |
BufferedOutputStream | Buffers output for performance. | Writing logs or large file. | new BufferedOutputStream(new FileOutputStream("log.txt")) |
DataOutputStream | Writes Java primitives (int, float). | Save structured binary data. | new DataOutputStream(new FileOutputStream("numbers.dat")) |
ObjectOutputStream | Serializes Java objects to a stream. | Save app state to disk. | new ObjectOutputStream(new FileOutputStream("user.ser")) |
Reader Hierarchy (Character Input)
Class | One-liner Definition | Real-Time Use Case | Code Example |
Reader (abstract class) | Base class to read character data. | Superclass for all character input readers. | — |
CharArrayReader | Reads from a char array as a stream. | Parse in-memory XML config. | new CharArrayReader(charArray) |
StringReader | Reads characters from a string. | Simulate file input for testing. | new StringReader("test input") |
FileReader | Reads characters from a file. | Read plain text file. | new FileReader("input.txt") |
BufferedReader | Reads text efficiently with buffer. | Read large text files line-by-line. | new BufferedReader(new FileReader("notes.txt")) |
InputStreamReader | Converts bytes to characters. | Read UTF-8 encoded file. | new InputStreamReader(new FileInputStream("utf.txt"), "UTF-8") |
LineNumberReader | Keeps track of line numbers. | Parse code files with line numbers. | new LineNumberReader(new FileReader("code.java")) |
Writer Hierarchy (Character Output)
Class | One-liner Definition | Real-Time Use Case | Code Example |
Writer (abstract class) | Base class to write character data. | Superclass for all character writers. | — |
CharArrayWriter | Writes to a character array. | Create temporary in-memory content. | new CharArrayWriter() |
StringWriter | Writes to a string buffer. | Build a string with writer methods. | new StringWriter() |
FileWriter | Writes characters to file. | Save user notes in text format. | new FileWriter("output.txt") |
BufferedWriter | Buffers characters for efficient writing. | Writing logs or large files. | new BufferedWriter(new FileWriter("log.txt")) |
OutputStreamWriter | Converts characters to bytes. | Write with specified encoding (e.g. UTF-8). | new OutputStreamWriter(new FileOutputStream("out.txt"), "UTF-8") |
PrintWriter | Prints formatted text (like System.out). | Log formatted messages easily. | new PrintWriter("log.txt") |
Other Classes
Class/Interface | One-liner Definition | Real-Time Use Case | Code Example |
File (class) | Represents a file or directory. | Check file properties, list directory. | new File("data.csv").exists() |
RandomAccessFile | Read/write to file at random positions. | Implementing file-based database. | new RandomAccessFile("log.txt", "rw") |
Serializable | Marks object to be serializable. | Persist or transfer objects. | class User implements Serializable |
Java NIO Hierarchy (java.nio)
|── Buffer (abstract class) – Core container for data transfer
| |── ByteBuffer (class) – Stores byte data
| |── CharBuffer (class) – Stores char data
| |── ShortBuffer (class) – Stores short values
| |── IntBuffer (class) – Stores int values
| |── LongBuffer (class) – Stores long values
| |── FloatBuffer (class) – Stores float values
| |── DoubleBuffer (class) – Stores double values
|── Charset (final class) – Encodes/decodes between byte and character sets
|── CharsetDecoder (abstract class) – Converts bytes to characters
|── CharsetEncoder (abstract class) – Converts characters to bytes
|── Channel (interface) – Base for all channels
| |── ReadableByteChannel (interface) – Reads byte data
| |── WritableByteChannel (interface) – Writes byte data
| |── ByteChannel (interface) – Combines readable and writable
| | |── SeekableByteChannel (interface) – Supports random access
| | | |── FileChannel (class) – File I/O with random access (blocking only)
| | | |── AsynchronousFileChannel (class) – Async file operations (non-blocking)
| |── SocketChannel (class) – TCP client channel (extends SelectableChannel)
| |── ServerSocketChannel (class) – TCP server channel (extends SelectableChannel)
| |── DatagramChannel (class) – UDP datagram channel (extends SelectableChannel)
|── SelectableChannel (abstract class) – Supports non-blocking I/O via Selector
| |── SocketChannel
| |── ServerSocketChannel
| |── DatagramChannel
| |── Pipe.SourceChannel
| |── Pipe.SinkChannel
|── Pipe (class) – Communication between threads
| |── Pipe.SourceChannel (class) – Read end (extends SelectableChannel)
| |── Pipe.SinkChannel (class) – Write end (extends SelectableChannel)
|── Selector (class) – Monitors multiple SelectableChannels for I/O events
|── SelectionKey (abstract class) – Represents registration of a channel with Selector
|── Channels (final class) – Utility methods to work with Channels and Streams
|── InterruptibleChannel (interface) – Allows async close of a blocking channel
|── Path (interface) – Represents a file/directory path
|── Files (final class) – Utility methods for file and directory I/O
|── Paths (final class) – Factory for creating Path instances
|── WatchService (interface) – Monitors file system changes
| |── WatchKey (interface) – Represents watch registration
| |── WatchEvent<T> (interface) – Represents file change event
|── DirectoryStream (interface) – Iterable for directory entries
|── FileSystem (abstract class) – Represents a file system
|── FileSystems (final class) – Access to available file systems
Buffer Classes
Class | One-liner Definition | Real-Time Use Case | Code Example |
ByteBuffer | Stores binary data for NIO operations. | Read/write file in chunks. | ByteBuffer buffer = ByteBuffer.allocate(1024); |
CharBuffer | Stores character data for NIO. | Char conversion without IO. | CharBuffer cb = CharBuffer.allocate(100); |
Channel Interfaces
Class | One-liner Definition | Real-Time Use Case | Code Example |
FileChannel | Reads/writes files via ByteBuffer. | High-performance I/O. | FileChannel fc = FileChannel.open(path); |
SocketChannel | Reads/writes data over TCP connection. | Build non-blocking client. | SocketChannel sc = SocketChannel.open(); |
ServerSocketChannel | Listens for TCP connections. | Build scalable NIO server. | ServerSocketChannel ssc = ServerSocketChannel.open(); |
File System Classes
Class/Interface | One-liner Definition | Real-Time Use Case | Code Example |
Path (interface) | Represents file path. | Replaces File in modern I/O. | Path path = Paths.get("data.txt"); |
Files (class) | Utilities to work with Path. | Copy, read, write, delete files. | Files.copy(p1, p2); |
WatchService | Watches file system for changes. | Auto-refresh on file changes. | watcher = FileSystems.getDefault().newWatchService(); |
Selector (class) | Manages multiple channels (I/O multiplexing). | Build concurrent servers. | Selector selector = Selector.open(); |
When to use which (Classic vs NIO)
Scenario | Use Classic I/O | Use NIO |
Simple file read/write | ✓ | ✓ |
Serialization | ✓ (ObjectOutputStream) | х |
Random file access | ✓ (RandomAccessFile) | ✓ (FileChannel) |
Memory-mapped file | х | ✓ (FileChannel.map()) |
High-performance server | х | ✓ (Selector, SocketChannel) |
File watching | х | ✓ (WatchService) |
Reading all lines as Stream | х | ✓ (Files.lines(path)) |
Real-Time Java I/O Examples and Use Cases
1. File – Check if a file exists and create it
File file = new File("data.txt");
if (!file.exists()) {
file.createNewFile();
}
2. BufferedReader – Read file line-by-line
try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
3. ObjectOutputStream – Serialize an object
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("obj.ser"))) {
out.writeObject(new Employee("Sameer", 123));
}
4. RandomAccessFile – Modify file content at position
RandomAccessFile raf = new RandomAccessFile("data.txt", "rw");
raf.seek(5); // Move to byte 5
raf.writeUTF("inserted");
raf.close();
Real-Time Java NIO Examples and Use Cases
a) Read and write using Path & Files
Path path = Paths.get("sample.txt");
Files.writeString(path, "Hello NIO");
String content = Files.readString(path);
System.out.println(content);
b) List files in directory
try (DirectoryStream<Path> stream = Files.newDirectoryStream(Paths.get("docs"))) {
for (Path entry : stream) {
System.out.println(entry.getFileName());
}
}
c) FileChannel – Random access read/write
try (RandomAccessFile raf = new RandomAccessFile("file.txt", "rw")) {
FileChannel channel = raf.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(48);
channel.read(buffer);
buffer.flip();
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
}
d) WatchService – Monitor file changes
WatchService watchService = FileSystems.getDefault().newWatchService();
Path dir = Paths.get("watched");
dir.register(watchService, StandardWatchEventKinds.ENTRY_CREATE);
WatchKey key = watchService.take();
for (WatchEvent<?> event : key.pollEvents()) {
System.out.println("File created: " + event.context());
}
e) Non-blocking ServerSocketChannel
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress(5000));
serverChannel.configureBlocking(false);
Selector selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
Best Practices
Area | Best Practice |
Resource Handling | Always close streams using try-with-resources |
Performance | Use buffered streams for large I/O |
Character Encoding | Specify encoding explicitly: new InputStreamReader(in, UTF_8) |
Error Handling | Catch and log exceptions meaningfully |
Use NIO for modern needs | Use Files, Path, WatchService instead of legacy File where possible |
Avoid File Locking Issues | Use FileChannel with lock() method for concurrent writes |
Prefer Streams | Use Files.lines(path).filter(...) for functional-style processing |
Summary Comparison – Classic vs NIO
Feature | Legacy I/O (java.io) | NIO (java.nio) |
API Style | Stream-based | Buffer + Channel |
Blocking | Always | Can be non-blocking |
Performance | Less efficient | Faster with large data |
File Operations | Manual | Files.* utilities |
Monitoring File System | Not supported | Supported via WatchService |
Memory Mapping | Not supported | Supported via MappedByteBuffer |
Stream vs Buffer vs Channel Comparison
Feature | Stream (java.io) | Buffer (java.nio) | Channel (java.nio.channels) |
Used In | Classic I/O (Blocking) | NIO (Non-blocking, buffer-based) | NIO (Non-blocking, selectable) |
Type | Abstraction over data flow | Container for data | Connection to I/O entities (file, socket) |
Direction | One-way (InputStream/OutputStream) | N/A (data holder) | Typically bi-directional |
How it Works | Data is read/written byte-by-byte or char-by-char | Data is first stored in buffer, then read/written in bulk | Works with Buffer to transfer data efficiently |
Efficiency | Less efficient for large I/O | Helps improve performance | High performance with selectors |
Blocking? | Blocking I/O | Non-blocking data storage | Supports non-blocking I/O |
Control Over Data | Little – reads/writes sequentially | More – has position, limit, capacity | High – supports selectors, multiplexing |
Examples | InputStream, Reader, FileOutputStream | ByteBuffer, CharBuffer | FileChannel, SocketChannel, DatagramChannel |
- Stream: A flow of data (bytes or characters) from a source to a destination, used in traditional (blocking) I/O.
- Buffer: A memory block that temporarily stores data during transfer between entities, used in Java NIO.
- Channel: A bi-directional communication path (like a pipe) for reading/writing data, used with NIO and operates with buffers.
Stream Like a straw – you suck data in a fixed stream - Buffer
Like a glass – you fill it once and drink multiple times - Channel
Like a pipe – allows two-way flow, can open/close, multiplex
Java I/O Comparison: Best Use Cases
Component | Best Use Cases |
InputStream / OutputStream | Reading/writing binary data such as images, audio, video, PDFs, ZIPs from files, sockets, or network sources. |
Reader / Writer | Reading/writing character/text data from files, network, or console (supports Unicode characters). |
BufferedInputStream / BufferedReader | Reading large data chunks efficiently. Reduces system calls by buffering input. Great for large file reads or line-by-line text reading. |
BufferedOutputStream / BufferedWriter | Efficient writing to disk or socket. Use when writing large text or binary data. |
DataInputStream / DataOutputStream | Reading and writing Java primitive types (int, long, double, etc.) in a portable binary format. |
ObjectInputStream / ObjectOutputStream | Serialization: Saving and restoring objects across files, network or for caching. (e.g., saving game state or object graph). |
ByteArrayInputStream / ByteArrayOutputStream | Temporary in-memory buffer for binary data. Often used in compression, encryption, or when mocking streams in testing. |
CharArrayReader / CharArrayWriter | Same as above but for character data. Useful in templating, XML/HTML generation. |
FileReader / FileWriter | Simple file reading/writing of text files with character data. Suitable for small to medium-sized files. |
FileInputStream / FileOutputStream | Reading/writing raw binary file data, especially for media files. Also used as base for wrapping with buffering or decoding. |
PipedInputStream / PipedOutputStream | Inter-thread communication. Implementing producer-consumer pattern between two threads via a stream. |
Scanner | Parsing structured text from input sources (e.g., parsing user input, log files, CSVs). Good for interactive CLI apps. |
PrintWriter / PrintStream | Writing formatted text easily (e.g., println, printf). Best for console output, log files, or writing formatted reports. |
Channel | Non-blocking, faster, and parallel access to data. Best for file I/O, network servers, and concurrent processing using Selector. |
Buffer (ByteBuffer, CharBuffer, etc.) | Holding temporary data in memory. Core part of NIO for efficient read/write between Channels. |
Selector | Managing multiple channels with a single thread. Ideal for scalable network servers (e.g., chat apps, HTTP servers). |
Memory-Mapped Files | Fast random access to large files (e.g., logs, DB files). Memory-efficient. Useful in Big Data or file-based caching. |
InputStreamReader / OutputStreamWriter | Bridging between bytes and characters (e.g., reading text from a byte stream using specific encoding like UTF-8). |
SequenceInputStream | Combine multiple streams into one. Useful for joining multiple file parts or streams together. |
PushbackInputStream / PushbackReader | For lookahead or rollback while parsing input (e.g., tokenizers, compilers, interpreters). |
- Classic IO (Stream/Reader): Simple, blocking, best for file-based or small-scale I/O.
- NIO (Channel/Buffer/Selector): Non-blocking, scalable, best for high-performance apps.
- Buffered Variants: Always wrap unbuffered streams for performance unless memory is a constraint.
- Object/Data Streams: Use when object or primitive preservation is needed across I/O.