Java Standard IO vs New IO

Java New input/output (NIO) was introduced with JDK 1.4. Picking up where Standard IO leaves, NIO provides high-speed, block-oriented IO to Java library.

By defining classes to hold data, and by processing that data in blocks, NIO takes advantage of low-level optimizations in a way that the java.io package could not, without using native code.

In this article, we will focus on identifying the most noticeable differences between Standard IO vs New IO which we must know before deciding which one to use in our next project.

Recalling Standard IO

Java IO refers to the interface between a computer and the rest of the world, or between a single program and the rest of the computer.

In Java programming, IO classes have until recently been carried out using a stream metaphor. All IO is viewed as the movement of single bytes, one at a time, through an object called a Stream.

Stream IO is used for contacting the outside world. It is also used internally, for turning objects into bytes and then back into objects. It is known as serialization and deserialization.

Introducing Java New IO

Java NIO was created to allow Java programmers to implement high-speed input-output operations without having to write custom native code.

NIO moves the most time-consuming I/O activities (namely, filling and draining buffers) back into the operating system, thus allowing for a great increase in speed.

If above introductions have left you thirsty then don’t worry if you will feel better as we go forward. Let’s start by finding the differences.

Differences between IO and NIO

IO Streams vs NIO Blocks

The most important distinction between the standard IO library (java.io.*) and New IO (java.nio.*) is how data is packaged and transmitted from the source to the target. As previously mentioned, standard I/O deals with the data in streams, whereas NIO deals with the data in blocks.

A stream-oriented I/O system deals with data one or more bytes at a time. An input stream produces one byte of data, and an output stream consumes one byte of data. It is very easy to create filters for streamed data. It is also relatively simply to chain several filters together so that each one does its part in what amounts to a single, sophisticated processing mechanism.

Important thing is that bytes are not cached anywhere. Furthermore, we cannot move forth and back in the data in a stream. If you need to move forth and back in the data read from a stream, we must cache it in a buffer first.

A block-oriented I/O system deals with data in blocks. Each operation produces or consumes a block of data in one step. Processing data by the block can be much faster than processing it by the (streamed) byte. You can move forth and back in the buffer as you need to.

Data blocks give us a bit more flexibility during processing. However, we also need to check if the buffer contains all the data we need in order to fully process it. And, we need to make sure that when reading more data into the buffer, we do not overwrite data in the buffer we have not yet processed.

On downside, block-oriented I/O lacks some of the elegance and simplicity of stream-oriented I/O.

Read more: 3 ways to read files using Java NIO

Synchronous Standard vs Asynchronous New IO

Java IO’s various streams are blocking or synchronous. That means, that when a thread invokes a read() or write() operation, that thread is blocked until there is some data to read, or the data is fully written. The thread will be in blocked state for this period. This has been cited as a good solid reason for bringing multi-threading in modern languages.

In asynchronous IO, a thread can request that some data be written to a channel, but not wait for it to be fully written. The thread can then go on and do something else in the mean time. Usually these threads spend their idle time on when not blocked in IO calls, is usually performing IO on other channels in the meantime. That is, a single thread can now manage multiple channels of input and output.

Synchronous programs often have to resort to polling, or to the creation of many, many threads, to deal with lots of connections. With asynchronous I/O, you can listen for I/O events on an arbitrary number of channels, without polling and without extra threads.

The central object in asynchronous I/O is called the Selector. A Selector is where you register your interest for various IO events, and it is the object that tells you when those events occur. So, the first thing we need to do is create a Selector.

Selector selector = Selector.open();

Later on, we will call the register() method on various Channel objects, in order to register our interest in IO events happening inside those objects. The first argument to register() is always the Selector.

Read more: How to define Path in java NIO

Java IO vs NIO APIs

No prize for guessing that the API calls when using NIO look different than when using IO. Here in NIO, rather than just reading the data byte for byte from e.g. an InputStream, the data must first be read into a Buffer, and then be processed from thereafter.

Java Example to read a file using Standard IO.

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
 
public class WithoutNIOExample
{
    public static void main(String[] args)
    {
        BufferedReader br = null;
        String sCurrentLine = null;
        try
        {
            br = new BufferedReader(
            new FileReader("test.txt"));
            while ((sCurrentLine = br.readLine()) != null)
            {
                System.out.println(sCurrentLine);
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        finally
        {
            try
            {
                if (br != null)
                br.close();
            } catch (IOException ex)
            {
                ex.printStackTrace();
            }
        }
    }
}

Java Example to read a file using New IO.

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
 
public class ReadFileWithFixedSizeBuffer
{
    public static void main(String[] args) throws IOException
    {
        RandomAccessFile aFile = new RandomAccessFile
                ("test.txt", "r");
        FileChannel inChannel = aFile.getChannel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        while(inChannel.read(buffer) > 0)
        {
            buffer.flip();
            for (int i = 0; i < buffer.limit(); i++)
            {
                System.out.print((char) buffer.get());
            }
            buffer.clear(); // do something with the data and clear/compact it.
        }
        inChannel.close();
        aFile.close();
    }
}

Conclusion

NIO allows you to manage multiple channels using only a single (or fewer) threads, but the cost is that parsing the data might be somewhat more complicated than when reading data from a blocking stream using standard IO.

If you need to manage thousands of open connections simultaneously, which each only send a little data, for instance a chat server, implementing the server in NIO is probably an advantage. Similarly, if you need to keep a lot of open connections to other computers, e.g. in a P2P network, using a single thread to manage all of your outbound connections might be an advantage.

If you have fewer connections with very high bandwidth, sending a lot of data at a time, standard IO server implementation should be your choice.

Happy Learning !!

Comments

Subscribe
Notify of
guest
2 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode