|
![]() |
![]() |
![]() |
![]() |
Channels were introduced in the 1.4 release of Java to provide a faster capability for input and output operations with files, network sockets, and piped I/O operations between programs than the methods provided by the stream classes. We will only be discussing channels in the context of files. The channel mechanism can take advantage of buffering and other capabilities of the underlying operating system and therefore is considerably more efficient than using the operations provided directly within the file stream classes. As we said earlier, a channel transfers data between a file and one or more buffers. We will take a quick look at the overall relationship between the various classes that define channels and buffers, and then look into the details of how you use channels with file streams.
There are a considerable number of classes and interfaces defining both channels and buffers. They also have similar names such as ByteBuffer and ByteChannel. Of course, File and file stream objects are also involved in file I/O operations so you will be using at least four different types of objects working together when you read from or write to files. Just to clarify what they all do, here's a summary of the essential role of each of them in file operations:
A File object encapsulates a path to a file or a directory, and such an object encapsulating a file path can be used to construct a file stream object.
A FileInputStream object encapsulates a file that can be read by a channel. A FileOutputstream object encapsulates a file that can be written by a channel. As we will see in the next chapter, a RandomAccessFile object can encapsulate a file that can be both read and written by a channel.
A buffer just holds data in memory. You load into a buffer what is to be written to a file using the buffer's put() methods. You use a buffer's get() methods to retrieve data that has been read from a file.
You obtain a FileChannel object from a file stream object or a RandomAccessFile object. A FileChannel object can read and write a file using read() and write() methods with a buffer or buffers as the source or destination of the data.
The channel interfaces and classes that we will be using are in the java.nio.channels package. The classes that define buffers are defined in the java.nio package. In a program that reads or writes files we will therefore need import statements for class names from at least three packages, the two packages we have just introduced plus the java.io package.
There are a series of channel interfaces, each of which declares a set of one or more related operations that a channel may perform. They all extend a common interface, Channel, which declares two methods:
The close() method that closes a channel
The isOpen() method that tests the state of the channel, returning true if it is open and false otherwise
Note that closing a channel does not necessarily close the file that the channel is attached to but closing a file also closes its channel. The channel interfaces are related as illustrated in the hierarchy shown below:
Each arrow points from a given interface to an interface that it implements. The ByteChannel interface simply combines the operations specified by the ReadableByteChannel and WritableByteChannel interface without declaring any additional methods. The ScatteringByteChannel interface extends the ReadableByteChannel interface by adding methods that allow data to be read and distributed amongst several separate buffers in a single operation. The GatheringByteChannel adds methods to those of the WritableByteChannel to permit writing from a number of separate buffers in a single operation.
The methods that each channel interface declares are as follows:
All of these methods can throw exceptions of one kind or another and we will go into details on these when we come to apply them. Note that a channel only works with buffers of type ByteBuffer. There are other kinds of buffers as we shall see, but you can't use them directly with the read() and write() methods for a channel. We will see what determines the number of bytes read or written in an operation when we discuss buffers in detail.
A FileChannel object defines a channel for a physical file, and provides an efficient mechanism for reading, writing, and manipulating the file. You can't create a FileChannel directly. You first have to create a file stream object for the file, then obtain a reference to the FileChannel object for the file by calling the getChannel() method for the file stream object. Here's how you would obtain the channel for a FileOutputStream object:
File aFile = new File("C:/Beg Java Stuff/myFile.text"); // Place to store an output stream reference FileOutputStream outputFile = null; try { // Create the stream opened to write outputFile = new FileOutputStream(aFile); } catch (FileNotFoundException e) { e.printStackTrace(System.err); System.exit(1); } // Get the channel for the file FileChannel outputChannel = outputFile.getChannel();
The FileChannel class implements all of the channel interfaces that we discussed in the previous section, so any FileChannel object incorporates the methods we have seen for both reading and writing a file. However, a FileChannel object obtained from a FileOutputStream object will not be able to read from the file since the stream only permits output. Similarly, a FileChannel obtained from a FileInputStream object can only read from the file. If you try to perform a read operation on a file opened just for output, a NonReadableChannelException will be thrown. Attempting to write to a file opened for input will result in a NonWritableChannelException being thrown.
Once you have obtained a reference to a file channel, you are ready to read from or write to the file, but we need to learn a bit more about buffers before we can try that out.
![]() |
![]() |
![]() |