JavaFileChannel Objects in Java, Background Information

FileChannel Objects in Java, Background Information

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Java Programming Notes # 1786


Preface


New features in SDK Version 1.4.0

The recently released JavaTM 2 SDK, Standard Edition Version 1.4 contains a number of new features.

Among the features which are new to version 1.4 is the concept
of an IO channel. Here is part of what Sun has to say about a channel:

“A channel represents an open connection to an entity such
as a hardware device, a file, a network socket, or a program
component that is capable of performing one or more distinct I/O
operations, …”

A channel is not the same as a stream.  Streams have been supported
by Java since the earliest days of Java.  Channels were not
introduced until version 1.4.

The java.nio.channels package

Much of the support for channels is provided in the package named java.nio.channels
This package defines more than a dozen new classes, and about seven new
interfaces, not counting the new exception classes.  (In addition,
there are four more new packages that begin with the name java.nio.)
 
Among the more interesting new classes in the java.nio.channels
package are the following:

  • Channels
  • DatagramChannel
  • FileChannel
  • Pipe.SinkChannel and Pipe.SourceChannel
  • ServerSocketChannel
  • SocketChannel

This list will give you some idea of the types of channel objects that
can be instantiated to support the new IO features of version 1.4.

There is much to learn

As you can readily see from the above list, there is much more to learn about
channels than can reasonably be included in a single lesson.

This article is
the first in a miniseries
that will deal mainly with the FileChannel class. The purpose of this
lesson is to introduce you to the concept
of channels (and other associated new IO features) from a read/write IO
viewpoint. 

Future lessons will walk
you through sample programs that use FileChannel objects to perform
read/write
file IO for different primitive data types as well as for data
records containing mixed primitive data types.

Future lessons will also provide background material for the use of channels from a memory
mapping viewpoint, and will walk you through one or more sample programs that
perform memory mapped IO using channels.

Future plans

As time goes on, I plan to publish additional lessons that will help
you learn how to use other new IO features including:

  • File locks
  • Character-set encoders and decoders
  • Pattern matching on files using regular expressions
  • Socket channels for both clients and servers
  • Non-blocking reads
  • Non-blocking servers

Viewing tip

You may find it useful to open another copy of this lesson in a separate
browser window.  That will make it easier for you to scroll back and
forth among the different listings and figures while you are reading about
them.

Supplementary material

I recommend that you also study the other lessons in my extensive collection
of online Java tutorials.  You will find those lessons published at
Gamelan.com
However, as of the date of this writing, Gamelan doesn’t maintain a consolidated
index of my Java tutorial lessons, and sometimes they are difficult to
locate there.  You will find a consolidated index at
www.DickBaldwin.com.

Discussion


The Buffer classes

Before getting into the details of FileChannel objects, I need
to tell you that much of what you will see in these lessons will involve objects
of classes with names like ByteBuffer, DoubleBuffer,
ShortBuffer
, etc.  These
classes extend the Buffer class, and as such inherit a number of methods
from the Buffer class.  In addition, the subclasses of Buffer
provide methods that are appropriate for objects instantiated from the
subclasses.

It will be much easier for you to understand this discussion of FileChannel
objects if you are already familiar with the features of Buffer
and ByteBuffer.  I have previously published lessons on these
topics entitled
Understanding
the Buffer class in Java
and
The
ByteBuffer Class in Java
.  You may find it useful to review these
two lessons in preparation for your study of this lesson.

What is a FileChannel?

Sun describes an object of the FileChannel class simply as

“A
channel for reading, writing, mapping, and manipulating a file.”
 

In this miniseries, I will show you how to use FileChannel objects for
reading files and writing files.  I will also show you how to
map files into memory.


Tied directly to ByteBuffer

FileChannel objects are tied directly to ByteBuffer objects. 
As mentioned earlier, the ByteBuffer class extends the Buffer
class.  Hence, a ByteBuffer object is a Buffer object.

A Buffer object is a “container for data of a specific primitive
type.”
 

Essential properties

According to Sun, “Aside from its content, the essential
properties of a buffer are its capacity, limit, and position.”
 
I discuss those three properties in some detail in the lesson on the Buffer
class that I referred to earlier, and won’t repeat that discussion here. 
However, for convenience, I will provide Sun’s definition of the position
property

“A buffer’s position is the index of the next element to
be read or written.”

The sample programs in this miniseries make extensive use of the value of the
position
property of a buffer.

FileChannel also has a position

Similarly, a FileChannel object also has a current position
within its file.  The current position of a FileChannel
object can be both queried
and modified.

Getting the FileChannel position

The current position of a FileChannel object can be queried
by invoking the overloaded position() method on the FileChannel object’s
reference.  This method returns the channel’s file position. 
This is a non-negative long integer counting the number of bytes
from the beginning of the file to the current position.

Setting the FileChannel position

Similarly, the current position of a FileChannel object
can be modified by invoking the overloaded position method
on the FileChannel object’s reference.  This overloaded version of
the method requires an
incoming integer parameter of type long and sets a new position
for the channel.

Be careful when setting the position

Care must be exercised when setting a new position for
the channel.  For example, here is what Sun says will happen if the
new position is greater than the file’s current size:

“Setting the position to a value that is greater than the
file’s current size is legal but does not change the size of the file.
A later attempt to read bytes at such a position will immediately return
an end-of-file indication. A later attempt to write bytes at such a position
will cause the file to be grown to accommodate the new bytes; the values
of any bytes between the previous end-of-file and the newly-written bytes
are unspecified.”

The physical file

The physical file associated with the FileChannel object contains
a variable-length sequence of bytes.  These bytes can be read and
written.  In addition, the current size of the file can be obtained
by invoking the size method on the FileChannel object’s reference. 
This method returns the current size of the physical file associated with
the channel, measured in bytes as a long integer.

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)
  • close()
  • isOpen()

Connection to ByteBuffer

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.

Expected behavior

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.

Thread safety

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.

Exceptions

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

Thread safety

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.

Thread safety

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.

File-specific operations

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.

Thread safety

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.

Other interfaces

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)

A scattering read operation reads, in a single invocation, a sequence
of bytes into one or more of a given sequence of buffers. 

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

The state of a FileChannel object

The state of a FileChannel object is connected to that of the
object whose getChannel method returned the reference to the
FileChannel object. 

Normally, changing the channel’s position,
whether explicitly or by reading or writing bytes, will change the file
position of the originating object, and vice versa. (An exception to
this rule will be discussed shortly.)

Similarly, changing the file’s length via the file channel will change
the length seen via the originating object, and vice versa.  Changing
the file’s content by writing bytes will change the content seen by the
originating object, and vice versa.

The fourth overloaded read method

The fourth overloaded read method of the FileChannel class
has the following signature:

read(ByteBuffer dst, long position)

This method reads a sequence of bytes from the channel into the given
buffer, starting at the specified file position.  (This is the exception
to the norm mentioned above.)

This method works the same as the read(ByteBuffer)
method, except that bytes are read starting at the specified file position
rather than at the channel’s current position. Furthermore, this method
does not modify the channel’s position.  If the specified position
is greater than the file’s current size then no bytes are read.

A fourth overloaded write method

A similar overloaded write method having the signature shown
below works the same as the write(ByteBuffer) method.  Again,
however, there is an exception to the normal operation described earlier. 
In this case, bytes are written starting at the specified file position
rather than at the channel’s current position.

write(ByteBuffer src, long position)

This method does not modify the channel’s position. If the specified position
is greater than the file’s current size then the file will be extended
to accommodate the new bytes.  Note, however, that the values of any
bytes between the previous end-of-file and the newly-written bytes are
unspecified.

Open for reading and writing

As I mentioned earlier, you can get a FileChannel object that
is connected to an underlying file by invoking the getChannel method
on an existing FileInputStream, FileOutputStream, or RandomAccessFile
object.

A FileChannel object obtained from a FileInputStream object
will be open for reading, but not for writing.  A FileChannel
object obtained from a FileOutputStream object will be open for
writing, but not for reading.

Finally, a FileChannel object obtained from a RandomAccessFile
object will be open for reading if the object was created in the “r”
mode, and will be open for reading and writing if the object was created
in the “rw” mode.

Append mode

If you create an instance of a FileOutputStream
object using the following constructor (passing true as a parameter), the
file will be opened in append mode.

FileOutputStream(File,boolean)

If you get a file channel from that object, the file channel will also be opened
in append mode.

According to Sun,

"In this mode each invocation of a relative write operation
first advances the position to the end of the file and then writes the requested
data."

Also according to Sun,

"Whether the advancement of the position
and the writing of the data are done in a single atomic operation is system-dependent and therefore unspecified."

Summary

A FileChannel object is a channel for reading, writing, mapping, and
manipulating a file.

FileChannel objects are tied directly to ByteBuffer
objects.  Therefore, you must understand the use of the Buffer class
and its subclasses in order to understand the use of the FileChannel
class.

A Buffer object is a container for data of a specific primitive
type.

The essential properties of a buffer are its capacity, limit,
and position.

A buffer’s position is the index of the next element to be read or
written.

A FileChannel object also has a current position
within its file, which can be both queried
and modified.

Care must be exercised when setting a new position for
a FileChannel object.

The physical file associated with the FileChannel object contains
a variable-length sequence of bytes, which may be read and
written.

The size of the physical file can be increased or reduced through truncation.

The FileChannel class implements several interfaces,
including the ByteChannel interface.

The ByteChannel interface
requires that the FileChannel class implement the following methods:

  • read(ByteBuffer dst)
  • write(ByteBuffer src)
  • close()
  • isOpen()

Any class that implements an interface must provide a concrete definition for
all the interface methods.  In addition, classes that implement the ByteChannel interface
are expected
to provide certain specific behavior for the read, write, and
close
methods.

Channels can be operated in a blocking or a non-blocking mode.

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, “File channels are safe for use by multiple concurrent
threads.”

In addition to the ByteChannel interface, the FileChannel
class implements the ScatteringByteChannel interface and the GatheringByteChannel
interface (plus several other interfaces as well).

The FileChannel class provides four overloaded versions of the
read method, only one of which is declared in the ByteChannel
interface.

Likewise, the FileChannel class provides four overloaded
versions of the write method, only one of which 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.  Two of the overloaded
write methods are declared in the GatheringByteChannel interface.

A scattering read operation reads, in a single invocation, a sequence
of bytes into one or more of a given sequence of buffers. 

A gathering write operation writes,
in a single invocation, a sequence of bytes from one or more of a given
sequence of buffers.

The fourth overloaded read method of the FileChannel class works the same as the read(ByteBuffer)
method, except that bytes are read starting at a specified file position.

The
fourth overloaded write method of the FileChannel class works the same as the write(ByteBuffer) method
except that bytes are written starting at a specified file position.

The FileChannel class does not define methods for opening existing
files or for creating new ones.

You can get a FileChannel object by invoking
the getChannel method on an existing file IO stream object.

A
FileChannel
object can be opened for reading, for writing, or for both
reading and writing.

A FileChannel object can also be opened for
writing in append mode.

What’s Next?

Future lessons in this miniseries will walk
you through sample programs that use FileChannel objects to perform
read/write
file IO for different primitive data types as well as for data
records containing mixed primitive data types.

For data of type byte, you will see the connection that exists
between the physical file, the FileChannel object, and a ByteBuffer
object.

For data of type double, you will see the connection that
exists between the physical file, the FileChannel object, a ByteBuffer
object, and a DoubleBuffer object that provides a double view of
data stored in the ByteBuffer object.

For data of type short, you will see the connection that
exists between the physical file, the FileChannel object, a ByteBuffer
object, and a ShortBuffer object that provides a short view of
data stored in the ByteBuffer object.

You will learn that view objects
such as DoubleBuffer and ShortBuffer are available for all the
primitive types other than boolean.

Thus, in addition to learning
about FileChannel objects, you will also learn about some additional
interesting aspects of the subclasses of the Buffer class.

Future lessons will also provide background material for the use of channels from a memory
mapping viewpoint, and will walk you through one or more sample programs that
perform memory mapped IO using channels.


Copyright 2002, Richard G. Baldwin.  Reproduction in whole or in
part in any form or medium without express written permission from Richard
Baldwin is prohibited.

About the author

Richard Baldwin
is a college professor (at Austin Community College in Austin, TX) and
private consultant whose primary focus is a combination of Java, C#, and
XML. In addition to the many platform and/or language independent benefits
of Java and C# applications, he believes that a combination of Java, C#,
and XML will become the primary driving force in the delivery of structured
information on the Web.

Richard has participated in numerous consulting projects, and he
frequently provides onsite training at the high-tech companies located
in and around Austin, Texas.  He is the author of Baldwin’s Programming
Tutorials,
which has gained a worldwide following among experienced and aspiring programmers.
He has also published articles in JavaPro magazine.

Richard holds an MSEE degree from Southern Methodist University and
has many years of experience in the application of computer technology
to real-world problems.

baldwin@DickBaldwin.com

-end-

 

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories