FileChannel Objects in Java, Background Information, Page 2
Increasing and reducing the file size
The size of the physical file increases when bytes are written beyond its current size. It is also possible to truncate the file by invoking the truncate method on the FileChannel object's reference, passing a long integer as a parameter to specify the new size.
If the new size is less than the file's current size then the file is truncated. In this case, any bytes beyond the new end of the file are discarded. If the new size is greater than or equal to the file's current size then the file is not modified. If the current position is greater than the new size, then the position is set to the new size.
The ByteChannel interface
The FileChannel class implements several interfaces. One of those interfaces is named ByteChannel. The ByteChannel interface inherits the following method declarations from the interfaces that it extends:
- read(ByteBuffer dst)
- write(ByteBuffer src)
This is where we see the first evidence of the connection between FileChannel objects and ByteBuffer objects. As you can guess from the names, the first two methods in the above list are used to read and write files. As you can see from the signature, these two methods require a reference to a ByteBuffer object as a parameter.
Any class that implements an interface must provide a concrete definition for all the interface methods. In addition, those classes that implement the ByteChannel interface (including DatagramChannel, FileChannel, and SocketChannel) are expected to provide the following behavior for the read method shown in the above list.
The read method
The read method reads a sequence of bytes from the channel into the ByteBuffer object whose reference is passed as a parameter to the method. In other words, the ByteBuffer passed as a parameter to the method is the destination for the data to be read from the channel.
(Thus, you might think of a channel object as a conduit through which data flows between a file and a ByteBuffer object.)
How much data gets read?
When the read method is invoked, an attempt will be made to read up to r bytes from the channel, where r is the number of bytes remaining in the buffer when the method is invoked. This value can be determined by invoking the remaining method on a reference to the buffer. (For more information on this, see the article referred to earlier that discusses the capacity, limit, and position properties of a buffer.)
(Note that the above description refers to the number of bytes remaining in the buffer, and not the number of bytes remaining in the channel.)
What is remaining?
Briefly, the remaining method returns the number of elements between the current value of the position property (of the buffer) and the current value of the limit property (of the buffer). As a result, when working with ByteBuffer objects, you must always be cognizant of the current position.
(For example, if you attempt to read bytes into a buffer whose position is equal to its limit, no data will be read into the buffer. This type of problem can be very difficult to identify when your program doesn't behave as you might have expected.)How does the read method affect the buffer properties?
Sun provides the following wording to explain the behavior of the read method as it relates to the position in the buffer, the limit of the buffer, the number of bytes remaining in the buffer, and the number of bytes to be read. I can't improve on this wording, so I will simply quote from the Sun documentation. In this description, r is the number of bytes remaining, n is the number of bytes read, and p is the current position
"Suppose that a byte sequence of length n is read, where 0<=n<=r. This byte sequence will be transferred into the buffer so that the first byte in the sequence is at index p and the last byte is at index p+n-1, where p is the buffer's position at the moment this method is invoked. Upon return the buffer's position will be equal to p+n; its limit will not have changed."How many bytes will be read?
I indicated earlier that when the read method is invoked, an attempt is made to read up to r bytes from the channel. Such a read operation may not fill the buffer. In fact it might not read any bytes at all. This will depend on the state of the buffer as well as the nature and state of the channel.
(For example, if position equals limit for the buffer, no data will actually be transferred. Also, a file channel cannot read any more bytes than remain in the file.)Blocking and non-blocking mode
Channels can be operated in a blocking or a non-blocking mode. This is a topic that I plan to discuss in detail in a future lesson. If a channel is in blocking mode and there is at least one byte remaining in the buffer, the read method will block until at least one byte is read.
The read method may be invoked at any time, and is thread-safe in the following sense. If another thread has already initiated a read operation on this channel, an invocation of the read method will block until the first operation is complete.
Return value from the read method
The read method returns the number of bytes actually read (which may be zero). You will see the use of this return value in the sample programs in the remaining lessons in this miniseries.
The method returns -1 if the channel has reached end-of-stream, which for a file channel would be the end of the file.
The read method throws several different types of exceptions, which I won't discuss in detail here. I will refer you to the Sun documentation for a discussion of those exceptions.
The write method
Any class that implements the ByteChannel interface (including FileChannel) is expected to provide the following behavior for the write method shown in the earlier list. The required signature of the write method is repeated below for convenience:
write(ByteBuffer src)A data source
In this case, you will note that the ByteBuffer object, (whose reference is passed as a parameter to the write method), provides the source of the data to be written to the channel. Thus, invocation of the write method writes a sequence of bytes to the channel from the specified buffer.
How many bytes are written?
As with the read method discussed earlier, when the write method is invoked on a FileChannel object, an attempt is made to write up to r bytes to the channel, where r is the number of bytes remaining in the buffer. As mentioned earlier, the number of remaining bytes can be determined by invoking the remaining method on the reference to the ByteBuffer object.
How does the write method affect the properties?
Again, here is how Sun explains the details of invoking the write method with respect to the various properties identified earlier.
"Suppose that a byte sequence of length n is written, where 0<=n<=r. This byte sequence will be transferred from the buffer starting at index p, where p is the buffer's position at the moment this method is invoked; the index of the last byte written will be p+n-1. Upon return the buffer's position will be equal to p+n; its limit will not have changed."When does the write method return?
According to Sun,
"Unless otherwise specified, a write operation will return only after writing all of the r requested bytes."So far, I haven't found any of the standard classes that implement this interface for which the documentation states that the write method will return early (with the possible exception of a socket channel in non-blocking mode, to be discussed in a future lesson).
As with the read method discussed earlier, the write method may be invoked at any time, and is thread-safe in the following sense. If another thread has already initiated a write operation on this channel, an invocation of the write method will block until the first operation is complete.
Return value and exceptions
The write method returns the number of bytes written, which may be zero. It also throws several exceptions. I will refer you to the Sun documentation for a description of those exceptions.
The close method
Any class that implements the ByteChannel interface (including FileChannel) is expected to provide the following behavior for the close method shown in the earlier list.
The close method takes no parameters and returns void. Invocation of the method on a channel closes the channel.
What does it mean to close the channel?
After the close method has been invoked on a channel, any further attempt to invoke IO operations on the channel will cause a ClosedChannelException to be thrown. Invoking the close method on a channel that is already closed has no effect.
As with the read and write methods discussed earlier, the close method may be invoked at any time. However, if some other thread has already invoked the close method on the channel, the second invocation will block until the first invocation is complete. When the first invocation is complete, the second invocation will return having no effect on the channel.
In addition to the read, write, and close methods applicable to all classes that implement the ByteChannel interface, the FileChannel class defines several file-specific operations.
- According to Sun, "Bytes may be read or written at an absolute position in a file in a way that does not affect the channel's current position."
- By invoking the map method on the channel, a region of a file may be mapped directly into memory. According to Sun, "for large files this is often much more efficient than invoking the usual read or write methods."
- By invoking the force method on the channel, updates made to a file may be forced out to the underlying storage device. This is a safety feature used to ensure that data are not lost in the event of a system crash.
- By invoking the transferFrom and transferTo methods, bytes can be transferred from a file to some other channel, and vice versa. According to Sun, this occurs "in a way that can be optimized by many operating systems into a very fast transfer directly to or from the filesystem cache."
- By invoking one of the overloaded lock methods on the channel, an entire file or a region of a file may be locked against access by other programs.
According to Sun, "File channels are safe for use by multiple concurrent threads." Sun goes on to describe the behavior when different threads attempt to concurrently access the same file for the same or different purposes. I will refer you to the Sun documentation for additional information on this topic, if the topic is of interest to you.
In addition to the ByteChannel interface, the FileChannel class implements the ScatteringByteChannel interface and the GatheringByteChannel interface (plus several other interfaces as well).
Overloaded read and write methods
The FileChannel class provides four overloaded versions of the read method and four overloaded versions of the write method.
In both cases, only one version of the overloaded methods is declared in the ByteChannel interface. Thus, the FileChannel class provides file-specific read and write capability over and above that declared in the ByteChannel interface.
Two of the four overloaded read methods are declared in the ScatteringByteChannel interface, and two of the overloaded write methods are declared in the GatheringByteChannel interface.
Reading and writing multiple buffers
The signatures of the overloaded versions of the read method that are declared in the ScatteringByteChannel interface are:
- read(ByteBuffer dsts)
- read(ByteBuffer dsts, int offset, int length)
Similarly, two overloaded write methods are provided to support gathering write operations. A gathering write operation writes, in a single invocation, a sequence of bytes from one or more of a given sequence of buffers.
Getting a FileChannel object
According to Sun,
"This class does not define methods for opening existing files or for creating new ones; such methods may be added in a future release."
You can get a FileChannel object by invoking
the getChannel method on an existing FileInputStream, FileOutputStream,
or RandomAccessFile object. This will return a reference
to a FileChannel object that is connected to the same underlying
Page 2 of 3