The NIO Classes
The NIO classes are contained in the packages shown here:
Before we begin, it is important to emphasize that the NIO subsystem does not replace the stream-based I/O classes found in java.io, which are discussed in Chapter 20, and good working knowledge of the stream-based I/O in java.io is helpful to understanding NIO.
The NIO system is built on two foundational items: buffers and channels. A buffer holds data. A channel represents an open connection to an I/O device, such as a file or a socket. In general, to use the NIO system, you obtain a channel to an I/O device and a buffer to hold data. You then operate on the buffer, inputting or outputting data as needed. The following sections examine buffers and channels in more detail.
Buffers are defined in the java.nio package. All buffers are subclasses of the Buffer class, which 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. Buffer defines several methods, which are shown in Table 21-1.
From Buffer, the following specific buffer classes are derived, which hold the type of data that their names imply:
MappedByteBuffer is a subclass of ByteBuffer and is used to map a file to a buffer.
All of the aforementioned buffers provide various get( ) and put( ) methods, which allow you to get data from a buffer or put data into a buffer. (Of course, if a buffer is read-only, then put( ) operations are not available.) Table 21-2 shows the get( ) and put( ) methods defined by ByteBuffer. The other buffer classes have similar methods. 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( ).
Channels are defined in java.nio.channels. A channel represents an open connection to an I/O source or destination. Channels implement the Channel interface. It extends Closeable, and it extends AutoCloseable. By implementing AutoCloseable, channels can be managed
Method : Description
abstract byte get( ) : Returns the byte at the current position.
ByteBuffer get(byte vals[ ]), int start, int num) : Copies the invoking buffer into the array referred to by vals. Returns a reference to the buffer. If there are not vals.length elements remaining in the buffer, a BufferUnderflowException is thrown.
ByteBuffer get(byte vals[ ], : Copies num elements from the invoking buffer into the array referred to by vals, beginning at the index specified by start. Returns a reference to the buffer. If there are not num elements remaining in the buffer, a BufferUnderflowException is thrown.
abstract byte get(int idx) : Returns the byte at the index specified by idx within the invoking buffer.
abstract ByteBuffer put(byte b) : Copies b into the invoking buffer at the current position. Returns a reference to the buffer. If the buffer is full, a BufferOverflowException is thrown.
final ByteBuffer put(byte vals[ ] ) : Copies all elements of vals into the invoking buffer, beginning at the current position. Returns a reference to the buffer. If the buffer cannot hold all of the elements, a BufferOverflowException is thrown.
ByteBuffer put(byte vals[ ], int start, int num) : Copies num elements from vals, beginning at start, into the invoking buffer. Returns a reference to the buffer. If the buffer cannot hold all of the elements, a BufferOverflowException is thrown.
ByteBuffer put(ByteBuffer bb) : Copies the elements in bb to the invoking buffer, beginning at the current position. If the buffer cannot hold all of the elements, a BufferOverflowException is thrown. Returns a reference to the buffer.
abstract ByteBuffer put(int idx, byte b) : Copies b into the invoking buffer at the location specified by idx. Returns a reference to the buffer.
Table 21-2 The get( ) and put( ) Methods Defined for ByteBuffer
with a try-with-resources statement. When used in a try-with-resources block, a channel is closed automatically when it is no longer needed. (See Chapter 13 for a discussion of try-with-resources.)
One way to obtain a channel is by calling getChannel( ) on an object that supports channels. For example, getChannel( ) is supported by the following I/O classes:
The specific type of channel returned depends upon the type of object getChannel( ) is called on. For example, when called on a FileInputStream, FileOutputStream, or
RandomAccessFile, getChannel( ) returns a channel of type FileChannel. When called on a Socket, getChannel( ) returns a SocketChannel.
Another way to obtain a channel is to use one of the static methods defined by the Files class. For example, using Files, you can obtain a byte channel by calling newByteChannel( ). It returns a SeekableByteChannel, which is an interface implemented by FileChannel. (The Files class is examined in detail later in this chapter.)
Channels such as FileChannel and SocketChannel support various read( ) and write( ) methods that enable you to perform I/O operations through the channel. For example, here are a few of the read( ) and write( ) methods defined for FileChannel.
All channels support additional methods that give you access to and control over the channel. For example, FileChannel supports methods to get or set the current position, transfer information between file channels, obtain the current size of the channel, and lock the channel, among others. FileChannel provides a static method called open( ), which opens a file and returns a channel to it. This provides another way to obtain a channel. FileChannel also provides the map( ) method, which lets you map a file to a buffer.
Charsets and Selectors
Two other entities used by NIO are charsets and selectors. A charset defines the way that bytes are mapped to characters. You can encode a sequence of characters into bytes using an encoder. You can decode a sequence of bytes into characters using a decoder. Charsets, encoders, and decoders are supported by classes defined in the java.nio.charset package. Because default encoders and decoders are provided, you will not often need to work explicitly with charsets.
A selector supports key-based, non-blocking, multiplexed I/O. In other words, selectors enable you to perform I/O through multiple channels. Selectors are supported by classes defined in the java.nio.channels package. Selectors are most applicable to socket-backed channels.
We will not use charsets or selectors in this chapter, but you might find them useful in your own applications.