Java-NIO

NIO

Beginning with version 1.4, Java has provided a second Input/Output system called NIO (which is short for New Input/Output). It supports a buffer-oriented, channel based approach to Input/Output operations. With the release of JDK 7, the NIO system was greatly expanded, providing enhanced support for file handling and file system features. In fact, so significant were the changes that the term NIO.2 is often used. Because of the capabilities supported by the NIO file classes, NIO is expected to become an increasingly important approach to file handling.

The NIO system is built on two foundational items: buffers and channels. Channels are analogous to streams in the original I/O package. A channel represents an open connection to an Input/Output device, such as a file or a socket. All data that goes anywhere (or comes from anywhere) must pass through a Channel object. A Buffer is essentially a container object. All data that  is sent to a channel must first be placed in a buffer; likewise, any data that is read from a channel is read into a buffer. In  general, to use the NIO system, you obtain a channel to an Input/Output device and a buffer to hold data. You then operate on the  buffer, inputting or outputting data as needed.

Buffers

A Buffer is an object, which holds some data, that is to be written to or that has just been read from. The addition of the Buffer object in NIO marks one of the most significant differences between the new library and original I/O. In stream-oriented I/O, you wrote data directly to, and read data directly from, stream objects.

In the NIO library, all data is handled with buffers. When data is read, it is read directly into a buffer. When data is written, it is written into a buffer. Anytime you access data in NIO, you are pulling it out of the buffer. A buffer is essentially an array. Generally, it is an array of bytes, but other kinds of arrays can be used. But a buffer is more than just an array. A buffer provides structured access to data and also keeps track of the system’s read/write processes.

Buffers are defined in the java.nio package. This package includes several abstract classes that extend Buffer, one for each primitive type except for Boolean: ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, and ShortBuffer. Buffer defines the core functionality common to all buffers: current position, limit, and capacity. The current position is the index within the buffer at which the next read or write operation will take place. The current position is advanced by most read or write operations. The limit is the index value one past the last valid location in the buffer. The capacity is the number of elements that the buffer can hold. Often the limit equals the capacity of the buffer. Buffer also supports mark and reset. Furthermore, this package includes MappedByteBuffer as an abstract ByteBuffer subclass.

Logical Layout of Buffer

Buffer Family Tree

buffer


Buffer Methods

Method Description
Object     array() If the invoking buffer is backed by an array, returns a reference to the array. Otherwise, an UnsupportedOperationException is thrown. If the array is read-only, a ReadOnlyBufferException is thrown.
int           arrayOffset() If the invoking buffer is backed by an array, returns the index of the first element. Otherwise, an UnsupportedOperationException is thrown. If the array is read-only, a ReadOnlyBufferException is thrown.
int           capacity() Returns the number of elements that the invoking buffer is capable of holding.
Buffer     clear() Clear this buffer. The position is set to 0, the limit is set to the capacity, and the mark is discarded. This method doesn’t erase the data in the buffer but is named as if it did because it will most often be used in situations in which that might as well be the case.
Buffer     flip() Flip this buffer. The limit is set to the current position and then the position is set to 0. When the mark is defined, it’s discarded.
boolean hasArray() Return true when this buffer is backed by an array and isn’t read-only; otherwise, return false. When this method returns true, array() and arrayOffset() may be invoked safely.
boolean hasRemaining() Returns true if there are elements remaining in the invoking buffer. Returns false otherwise.
boolean isDirect() Returns true if the invoking buffer is direct, which means I/O operations act directly upon it. Returns false otherwise.
boolean isReadOnly( ) Return true when this buffer is read-only, otherwise, return false.
int          limit( ) Returns the invoking buffer’s limit.
Buffer    limit(int n) Sets the invoking buffer’s limit to n. Returns a reference to the buffer.
Buffer    mark( ) Sets the mark and returns a reference to the invoking buffer.
int          position( ) Returns the current position.
Buffer     position(int n) Sets the invoking buffer’s current position to n. Returns a reference to the buffer
int           remaining( ) Returns the number of elements available before the limit is reached. In other words, it returns the limit minus the current position
Buffer     reset( ) Resets the current position of the invoking buffer to the previously set mark. Returns a reference to the buffer.
Buffer     rewind( ) Sets the position of the invoking buffer to 0. Returns a reference to the buffer.


Buffer Writing and Reading

All of the primitive buffers provide various get( ) and put( ) methods, which allow you to get data from a buffer or put data into a buffer. All buffer classes also support methods that perform various buffer operations. For example, you can allocate a buffer manually using allocate( ). You can wrap an array inside a buffer using wrap( ). You can create a subsequence of a buffer using slice( ).

ByteBuffer

The most commonly used kind of buffer is the ByteBuffer. A ByteBuffer allows get/set operations (that is, the getting and setting of bytes) on its underlying byte array. None of buffer classes can be instantiated directly. They are all abstract classes, but each contains static factory methods to create new instances of the appropriate class.

New buffers are created by either allocation or wrapping. Allocation creates a buffer object and allocates private space to hold capacity data elements. Wrapping creates a buffer object but does not allocate any space to hold the data elements. It uses the array you provide as backing storage to hold the data elements of the buffer. To allocate a ByteBuffer capable of holding 10 bytes :

allocate

This line creates a byte buffer with an internal byte array that stores a maximum of 10 bytes. If you want to provide your own array to be used as the buffer’s backing store, call the wrap( ) method :

wrap

This constructs a new buffer object, but the data elements will live in the array. This implies that changes made to the buffer by invoking put( ) will be reflected in the array, and any changes made directly to the array will be visible to the buffer object.

Flipping

When you write data into a buffer, the buffer keeps track of how much data you have written. Once you need to read the data, you need to switch the buffer from writing mode into reading mode using the flip() method call. Calling flip() sets the position back to 0, and sets the limit to where position just was.

In other words, position now marks the reading position, and limit marks how many bytes were written into the buffer-the limit of how many bytes that can be read. In reading mode the buffer lets you read all the data written into the buffer.

Many of Buffer’s methods return Buffer references so that you can chain instance method calls together. For example, instead of specifying the following three lines :

chaind

you can more conveniently specify the following line :

chined
Program

Program Source

import java.nio.ByteBuffer;

public class Javaapp {

    public static void main(String[] args) {
        
        ByteBuffer buf = ByteBuffer.allocate(12);
        buf.put((byte)10);
        buf.put((byte)20).put((byte)30);
        buf.put(new byte[]{40,50,60});
        
        byte by[] = {30,40,50,60,70,80,90,100};
        buf.put(by, 4, 4);
        System.out.println("Po : "+buf.position());
        System.out.println("Li : "+buf.limit());
        System.out.println("Ca : "+buf.capacity());
        buf.flip();
        System.out.println("Po : "+buf.position());
        System.out.println("Li : "+buf.limit());
        System.out.println("Ca : "+buf.capacity());
        
        for(int i=0;i<buf.limit();i++)
        {
            System.out.print(buf.get()+" ");
        }
    }
}

Leave a Comment