Java I/O and NIO


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

 | InputStream (abstract class, byte input)  Read bytes 
               i.e., Reads raw data 8 bits (1 byte) at a time from a source like File (e.g., PDF, image, zip), Network socket, Keyboard input, Memory buffer.

 |    |─ 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

 | Reader (abstract class, character input) – Reads characters 
               i.e., Reads 16-bit Unicode characters (e.g. string, number, special character) from character-based sources like Text files (.txt, .csv), Strings, arrays, Input streams.

 |    |─ 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)


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

java.nio.charset
 | Charset (final class) – Encodes/decodes between byte and character sets
 | CharsetDecoder (abstract class) – Converts bytes to characters
 | CharsetEncoder (abstract class) – Converts characters to bytes

java.nio.channels
 | 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

java.nio.file
 | 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.
Real-World Analogy
  • StreamLike 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).



Tips:
  • 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.