Tuesday 28 March 2017

The New I/O Packages - Java Tutorials ( Page 1 of 2 )

Java 2, version 1.4 added a new way to handle I/O operations. Called the new I/O APIs, it is one of the more interesting additions that Sun included in the 1.4 release because it supports a channel-based approach to I/O operations. The new I/O classes are contained in the five packages shown here.


Package -- Purpose

java.nio:  Top-level package for the new I/O system. Encapsulates various types of buffers which contain data operated upon by the new I/O system.

java.nio.channels:  Supports channels, which are essentially open I/O connections.

java.nio.channels.spi:  Supports service providers for channels.

java.nio.charset:  Encapsulates character sets. Also supports encoders and decoders that convert characters to bytes and bytes to characters, respectively.

java.nio.charset.spi:  Supports service providers for character sets.

Before we begin, it is important to emphasize that the new I/O subsystem (NIO) is not intended to replace the I/O classes found in java.io. Instead, the NIO classes supplement the standard I/O system, giving you an alternative approach, which can be beneficial in some circumstances.


NIO Fundamentals

The new I/O 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 new I/O 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
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 of the end of the buffer. The capacity is the number of elements that the buffer can hold. Buffer also supports mark and reset. Buffer defines several methods, which are shown below.


The methods defined by Buffer

final int capacity( ):  Returns the number of elements that the invoking buffer is capable of holding.

final Buffer clear( ):  Clears the invoking buffer and returns a reference to the buffer.

final Buffer flip( ):  Sets the invoking buffer’s limit to the current position and resets the current position to 0. Returns a reference to the buffer.

final boolean hasRemaining( ):  Returns true if there are elements remaining in the invoking buffer. Returns false otherwise.

abstract boolean isReadOnly( ):  Returns true if the invoking buffer is read-only. Returns false otherwise.

final int limit( ):  Returns the invoking buffer’s limit.

final Buffer limit(int n):  Sets the invoking buffer’s limit to n. Returns a reference to the buffer.

final Buffer mark( ):  Sets the mark and returns a reference to the invoking buffer.

final int position( ):  Returns the current position.

final Buffer position(int n):  Sets the invoking buffer’s current position to n. Returns a reference to the buffer.

final Buffer reset( ):  Resets the current position of the invoking buffer to the previously set mark. Returns a reference to the buffer.

final Buffer rewind( ):  Sets the position of the invoking buffer to 0. Returns a reference to the buffer.


From Buffer are derived the following specific buffer classes, which hold the type
of data that their names imply.
  • ByteBuffer 
  • CharBuffer 
  • DoubleBuffer 
  • FloatBuffer
  • IntBuffer 
  • LongBuffer 
  • MappedByteBuffer 
  • ShortBuffer

MappedByteBuffer is a subclass of ByteBuffer that is used to map a file to a buffer.

All buffers support various get( ) and put( ) methods, which allow you to get data from a buffer or put data into a buffer. For example, below 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( ).


The get( ) and put( ) methods defined for ByteBuffer

abstract byte get( ):  Returns the byte at the current position.

ByteBuffer get(byte vals[ ] ):  Copies the invoking buffer into the array referred to by vals. Returns a reference to the buffer.

ByteBuffer get(byte vals[ ], int start, int num):  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.

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.

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.


Channels
Channels are defined in java.nio.channels. A channel represents an open connection to an I/O source or destination. You obtain a channel by calling getChannel( ) on an object that supports channels. Java 2, version 1.4 added getChannel( ) to the following I/O classes.
  • FileInputStream 
  • FileOutputStream 
  • RandomAccessFile
  • Socket 
  • ServerSocket 
  • DatagramSocket

Thus, to obtain a channel,  you first obtain an object of one of these classes and then call  getChannel ( ) on that object.

The specific type of channel returned depends upon the type of object getChannel( ) is called on. For example, when called on a FileInputStream, FileOuputStream, or RandomAccessFile, getChannel( ) returns a channel of type FileChannel. When called on a Socket, getChannel( ) returns a SocketChannel.

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 can throw an IOException.


Method  --  Description

abstract int read(ByteBuffer bb):  Reads bytes from the invoking channel into bb until the buffer is full, or there is no more input. Returns the number of bytes actually read.

abstract int read(ByteBuffer bb, long start):  Beginning at the file location specified by start, reads bytes from the invoking channel into bb until the buffer is full, or there is no more input. The current position is unchanged. Returns the number of bytes actually read, or –1 if start is beyond the end of the file.

abstract int write(ByteBuffer bb):  Writes the contents of bb to the invoking channel, starting at the current position. Returns the number of bytes written.

abstract int write(ByteBuffer bb, long start):  Beginning at the file location specified by start, writes the contents of bb to the invoking channel. The current position is unchanged. Returns the number of bytes written.

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


Using the New I/O System

Because the most common I/O device is the disk file, the rest of this section examines how to access a disk file using the new I/O system. Because all file channel operations are byte-based, the type of buffers that we will be using are of type ByteBuffer.

Reading a File
There are several ways to read data from a file using the new I/O system. We will look at two. The first reads a file by manually allocating a buffer and then performing an explicit read operation. The second uses a mapped file, which automates the process. To read a file using a channel and a manually allocated buffer, follow this procedure. First open the file for input using FileInputStream. Then, obtain a channel to this file by calling getChannel( ). It has this general form:

      FileChannel getChannel( )

It returns a FileChannel object, which encapsulates the channel for file operations. Once a file channel has been opened, obtain the size of the file by calling size( ), shown here:

      long size( ) throws IOException

It returns the current size, in bytes, of the channel, which reflects the underlying file. Next, call allocate( ) to allocate a buffer large enough to hold the file’s contents. Because file channels operate on byte buffers you will use the allocate( ) method defined by ByteBuffer. It has this general form.

      static ByteBuffer allocate(int cap)

Here, cap specifies the capacity of the buffer. A reference to the buffer is returned. After you have created the buffer, call read( ) on the channel, passing a reference to the buffer. The following program shows how to read a text file called test.txt through a channel using explicit input operations.

  // Use the new I/O system to read a text file.
  import java.io.*;
  import java.nio.*;
  import java.nio.channels.*;

  public class ExplicitChannelRead {
    public static void main(String args[])
      FileInputStream fIn;
      FileChannel fChan;
      long fSize;
      ByteBuffer mBuf;

      try
        // First, open a file for input.
        fIn = new FileInputStream("test.txt");

        // Next, obtain a channel to that file.
        fChan = fIn.getChannel();

        // Now, get the file's size.
        fSize = fChan.size();

        // Allocate a buffer of the necessary size.
        mBuf = ByteBuffer.allocate((int)fSize);

        // Read the file into the buffer.
        fChan.read(mBuf);

        // Rewind the buffer so that it can be read.
        mBuf.rewind();

        // Read bytes from the buffer.
        for(int i=0; i < fSize; i++)
          System.out.print((char)mBuf.get());

        System.out.println();

        fChan.close(); // close channel
        fIn.close(); // close file
      } catch (IOException exc) {
        System.out.println(exc);
        System.exit(1);
      }
    }
  }

No comments:

Post a Comment