JavaFileChannel Objects in Java, ByteBuffer Type

FileChannel Objects in Java, ByteBuffer Type

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 # 1788


Preface


New features in SDK Version 1.4.0

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

Among the features which are completely new to version 1.4 is the concept
of an IO channel.  The previous lesson, entitled

FileChannel Objects in Java, Background Information
, introduced you to the concept of channels from a read/write IO viewpoint.

Data of type byte

This lesson will walk you
through a sample program that uses FileChannel objects in conjunction
with ByteBuffer objects to perform
read/write file IO for data of the primitive type byte.

Mixed primitive types

In the next lesson, I will show you how to use the new I/O classes to transfer data of type
double and data of type short between the computer’s memory and a physical file.  You will
learn how to extend the concept to any primitive data type other than boolean. 

In a future lesson, I will show you how to use the new I/O classes to create records
consisting of sequences of data values of different primitive types, and how to
transfer those records between the computer’s memory and a physical file.

Memory-mapped IO

Future lessons will teach you how to do memory-mapped IO using channels.

A channel is not a stream

Note
that a channel is not the same as a stream, but a channel may be based on
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.)

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
and Sample Code


The Buffer and ByteBuffer classes

Before getting into the details of FileChannel objects, I need
to tell you that much of what you will see in this lesson will involve
objects of the ByteBuffer class.  The ByteBuffer class
extends the Buffer class, and as such inherits a number of methods
from the Buffer class.

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 those
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 lesson, I will show you how to use FileChannel objects for
reading files and writing files.  In a future lesson, I will show you how
to use FileChannel objects for mapping files into memory.

Enough talk, let’s see some code!

I will illustrate the FileChannel class using the sample program named Channel01

You will find a complete listing
of the program in Listing 16 near the end of the lesson.  As is my normal
approach, I will discuss this program in fragments.

This program, which
was tested using Java SDK version 1.4.0 under Win2000, first writes, and then reads a
file using FileChannel objects.  The data written to and read from
the file is of the primitive type byte.

Import
directives

The first fragment, shown in Listing 1, shows the beginning of the class named
Channel01.  The primary purpose of showing this listing at this
point is to emphasize the use of the new java.nio packages, as
illustrated by the import directives.

import java.io.*;
import java.nio.channels.*;
import java.nio.*;

class Channel01{

Listing 1

The showBufferData method

Listing 2 shows the signature of a static convenience method of my own
design named showBufferData.  As you might guess from the name and
the incoming parameter type, the
purpose of this method is to display the contents of a ByteBuffer object. 
This method will be used later in the program, so you need to understand how it
behaves.

  static void showBufferData(
          ByteBuffer buf, String name){

Listing 2

A ByteBuffer parameter

As you can see, this method receives a reference to a ByteBuffer
object as an incoming parameter.  The method displays the byte data
encapsulated by that
object.

A String parameter

The method also receives a reference to a String object as an incoming
parameter.  This string is included in the display to make it easier to
associate the output with the program code.

Saving the position property

When the ByteBuffer object is passed to the method, its position
property may or may not be zero.  This method needs to be careful to avoid
modifying the value of the position property.  Therefore, the method
saves the value of the position property upon entry and restores that
value upon exit.

    int pos = buf.position();

    buf.position(0);

    System.out.println(
                   "Data for " + name);

Listing 3

The code in Listing 3 begins by invoking one of the overloaded versions of
the position method to get and save the value of the position
property. 

Set position property to zero

Having saved the value of the position property, the code in Listing 3
invokes the other overloaded version of the position method to set the
value of the position property to zero.

(Those of you who are familiar with JavaBeans design patterns will
recognize that these overloaded versions of the position property do not
adhere to the design pattern for property getter and setter methods.)

Display a header

Finally, the code in Listing 3 displays a header for the data that will
follow, and identifies that data by including the value of the incoming string
named name.

Iterating on a ByteBuffer object

The ByteBuffer class provides methods named hasRemaining
and get, which make it easy to iterate on the buffer.  The code in
Listing 4 uses these two methods in a while loop to read and display the
byte at the buffer’s current position, and then to automatically increment the position. 

    while(buf.hasRemaining()){
      System.out.print(
                      buf.get() + " ");
    }//end while loop

Listing 4

The hasRemaining method

The hasRemaining method in the conditional clause of the while
loop returns a boolean, which tells whether there are any elements
between the current position and the limit.  When the limit is reached, the
hasRemaining method returns false, and control exits the while
loop.

The relative get method

The get method used in Listing 4 gets and returns the byte at the
current position and automatically increments the value of the position
property.

Restoring the value of the position property

The code in Listing 5 displays a blank line to separate the blocks of output
data.  Then it restores the value of the position property, and
returns, ending the showBufferData method.

    System.out.println();//new line

    buf.position(pos);
  }//end showBufferData

Listing 5

The
clearBufferData method

Listing 6 shows the signature for a static convenience method named
clearBufferData
whose purpose is to store a zero value in each element of a
ByteBuffer object received as an incoming parameter.

  static void clearBufferData(
          ByteBuffer buf, String name){
    
Listing 6

The get and put methods of a ByteBuffer

In addition to the get method discussed above, the ByteBuffer
class also provides a method named put, which writes a given byte
value into the
buffer at the current position, and then increments the position.

Very similar to previous code

If you understood the behavior of the showBufferData method discussed
above, you should have no difficulty understanding the behavior of the
clearBufferData
method.  The body of that method is shown in its
entirety in Listing 7.

    //Set position to zero
    buf.position(0);

    System.out.println(
                      "Clear " + name);

    while(buf.hasRemaining()){
      buf.put((byte)0);
    }//end while loop

    //Set position to zero and return
    buf.position(0);
  }//end method

Listing 7

Now let’s talk about channels!

That covers the preliminaries.  We now have a convenient and easy way to
clear and/or display the contents of a ByteBuffer object, so we can get
down to the main topic of this lesson – channels.

For simplicity, the remaining code in this program is contained in the
main
method, which begins in Listing 8.

Create some raw data

After printing a text header on the screen, the main method in Listing
8 creates and populates a six-element byte[] array object.  This
will be the raw data for the operations that follow.

  public static void main(
                        String[] args){
    System.out.println(
         "Demo write/read on channel");

    byte[] array = {65,66,67,68,69,70};

Listing 8

Hopefully the code in Listing 8 will be familiar to you.  If not, you may
need to review some of my introductory Java tutorials, such as the one entitled

The Essence of OOP using Java, Array Objects, Part 1
.  That tutorial is followed by parts 2
and 3.

Creating a ByteBuffer object

The code in Listing 9 wraps the array object in a ByteBuffer object. 
This code illustrates one of the ways to create a ByteBuffer object in
Java.

(If this code is unfamiliar to you, you should probably review my tutorial
lessons entitled

Understanding the Buffer class in Java
and

The ByteBuffer Class in Java
.)

    ByteBuffer buf = 
                ByteBuffer.wrap(array);

    showBufferData(buf,"buf-raw data");

Listing 9

Display contents of ByteBuffer object

Having created a ByteBuffer object that wraps the array object, the
code in Listing 9 displays the contents of the ByteBuffer object by
invoking the showBufferData method discussed earlier.  The output
produced by the code in Listing 9 is shown in Figure 1.

Data for buf-raw data
65 66 67 68 69 70

Figure 1

As you would expect, the values shown in Figure 1 match the values stored in
the array object in Listing 8, which was wrapped in the ByteBuffer object
in Listing 9.

Now for the FileChannel class

We’ve finally reached the point where we are prepared to discuss the
FileChannel
class.  The code in Listing 10 begins by instantiating a
new object of the class FileOutputStream, and linking that object to a
physical file named junk.txt.

    try{
      FileChannel outCh = 
           (new FileOutputStream(
             "junk.txt")).getChannel();

Listing 10

Get a FileChannel object for output

Then the code in Listing 10 invokes the getChannel method on the
FileOutputStream
object’s reference to create a FileChannel object
linked to the same physical file as the
FileOutputStream
.

(This FileChannel object can be
used to write data to the physical file.  However, it cannot be used to
read data from the file because it is derived from an output stream.)

The reference to the FileChannel object is stored in a reference
variable of type FileChannel named outCh.

Write the buffer data to the physical file

The code in Listing 11 causes the data in the ByteBuffer object to be
written into the physical file by invoking the write method of the
FileChannel
class, passing the buffer object’s reference as a parameter.

      System.out.println(
                   "Bytes written = " 
                   + outCh.write(buf));

      outCh.close();

Listing 11

The number of bytes written

The write method returns the number of bytes written to the physical
file, which is displayed by the code in Listing 11.

The output produced by the code in Listing 11 is shown in Figure 2.

Bytes written = 6

Figure 2

As you would expect, the number of bytes written to the physical file matches
the number of bytes originally stored in the array object and later wrapped in
the ByteBuffer object.

Close the channel

Then the code in Listing 11 invokes the close method to close the
FileChannel
object.

Clear the buffer

The code in Listing 12 invokes the clearBufferData method
discussed earlier to store a zero value in each of the elements of the
ByteBuffer
object.

      clearBufferData(buf,"buf");

Listing 12

Note that when the clearBufferData method returns, the position
property of the buffer has been set to zero.

Invocation of the clearBufferData method produces the output shown in Figure 3.

Clear buf

Figure 3

Get a FileChannel object for input

The code in Listing 13 uses an instance of the FileInputStream class
as an intermediary to get a FileChannel object that is suitable for
reading data from the physical file named junk.txt.

      FileChannel inCh = 
           (new FileInputStream(
             "junk.txt")).getChannel();

Listing 13

(Note that this object cannot be used for output because it was derived from
an input stream.)

The FileChannel object’s reference is stored in the reference variable
named inCh.

Read data from physical file into the buffer

The code in Listing 14 invokes the read method on the FileChannel
object’s reference to read the data from the physical file into the
ByteBuffer
object, whose reference is passed as a parameter to the read
method.

(Recall that the elements of the ByteBuffer object were previously cleared
to all zero values.)

      System.out.println(
                     "Bytes read = " 
                     + inCh.read(buf));

      inCh.close();

Listing 14

The read method returns the number of bytes actually read, producing the
output shown in Figure 4.

Bytes read = 6

Figure 4

As you would probably expect, the number of bytes read matches the number of
bytes in the physical file, which matches the number of elements in the original
array object.

Recap

To recap, the ByteBuffer object was originally created and populated by wrapping
an array object of type byte[] having a length of six elements. An
output
FileChannel object was used to cause the contents of the ByteBuffer
object to be written into a physical file named junk.txt.

Then each of the elements in the ByteBuffer object was set to a value
of zero, and the position property for the ByteBuffer object was
set to zero.

Following this, an input FileChannel object was used to cause the
contents of the physical file named junk.txt to be read into the
ByteBuffer
object.

Display data in the ByteBuffer object

The code in Listing 15 causes the new contents of the ByteBuffer
object to be displayed on the computer screen.

      showBufferData(buf,"buf");

Listing 15

The output produced by the code in Listing 15 is shown in Figure 5.

Data for buf
65 66 67 68 69 70

Figure 5

Data matches the original array object

Since nothing was done to modify any of the data anywhere along the
way, the values of the
elements in the ByteBuffer object match the values of the elements in the
original array object, which was the starting point for this series of operations.

What have you learned?

You have learned how to use the write and read methods of the
FileChannel class to cause data of type byte to be transferred between a ByteBuffer
object and a physical file.  You have seen a simple example of why Sun says:

"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, …"

So what!

By now, you may be saying "So what!  There are other, possibly easier
ways to transfer data of type byte between the computer’s memory and a
physical file."

Good point

While this sample program illustrates the basics of using channels, it
doesn’t do a very good job of showing off the advantages of the new channel
capability in Java.  However, you need to understand the basics before
moving on to more complex topics. 

The sample program that I will discuss in the next lesson will do a little
better in terms of showing off the advantages of channels.

In the next lesson, I will show you how to use the FileChannel class, the
ByteBuffer
class, the DoubleBuffer class, and the ShortBuffer
class to transfer data of type
double and data of type short between the computer’s memory and a physical file.  You will
learn how to extend the concept to any primitive data type other than boolean. 

In a future lesson, I will show you how to use the FileChannel class along
with the ByteBuffer class to create records consisting of sequences of
data values of different primitive types, and how to transfer those records
between the computer’s memory and a physical file.

The examples in the next lesson will better illustrate some of the advantages of channels.

Run the Program


If you haven’t already done so, I encourage you to copy the code from Listing
16 into your text editor, compile it, and execute it.  Experiment
with it, making changes, and observing the results of your changes.

Remember, however, that you must be running Java version 1.4.0 or later
to compile and execute this program.

Summary

Sun describes an object of the FileChannel class simply as "A
channel for reading, writing, mapping, and manipulating a file."

FileChannel objects are tied directly to objects of the ByteBuffer
class.

One way to create a ByteBuffer object is to wrap an existing
array object in a ByteBuffer object.

You can get a FileChannel
object for output by invoking the getChannel method on a
FileOutputStream
object’s reference. The FileChannel object can be
used to write data to a physical file. However, it cannot be used to read data
from the file.

The write method of a FileChannel object actually
transfers data from a ByteBuffer object to a physical file.

You can get
a FileChannel object for input by invoking the getChannel method
on a FileInputStream object’s reference. The FileChannel object
can be used to read data from a physical file. However, it cannot be used to
write data to the file.

The read method of a FileChannel object
actually transfers data into a ByteBuffer object from a physical file.

What’s Next?

In the next lesson, I will show you how to use the new I/O classes to transfer data of type
double and data of type short between the computer’s memory and a physical file.  You will
learn how to extend the concept to any primitive data type other than boolean. 

In a future lesson, I will show you how to use the new I/O classes to create records
consisting of sequences of data values of different primitive types, and how to
transfer those records between the computer’s memory and a physical file.

As time goes on, I plan to publish additional lessons that will help
you learn 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

Complete Program Listing


A complete listing of the program discussed in this lesson is shown in
Listing 16 below.

 

/* File Channel01.java
Copyright 2002, R.G.Baldwin

Illustrates use of FileChannel objects.

First writes and then reads a file
using FileChannel objects.

Tested using JDK 1.4.0 under Win2000

The output is:

Demo write/read on channel
Data for buf-raw data
65 66 67 68 69 70
Bytes written = 6
Clear buf
Bytes read = 6
Data for buf
65 66 67 68 69 70

**************************************/

import java.io.*;
import java.nio.channels.*;
import java.nio.*;

class Channel01{

  static void showBufferData(
          ByteBuffer buf, String name){
    //Displays byte buffer contents
    
    //Save position
    int pos = buf.position();
    //Set position to zero
    buf.position(0);
    System.out.println(
            "Data for " + name);
    while(buf.hasRemaining()){
      System.out.print(
                      buf.get() + " ");
    }//end while loop
    System.out.println();//new line
    //Restore position and return
    buf.position(pos);
  }//end showBufferData
  //---------------------------------//
  
  static void clearBufferData(
          ByteBuffer buf, String name){
    //Stores 0 in each element of a
    // byte buffer.
    
    //Set position to zero
    buf.position(0);
    System.out.println(
            "Clear " + name);
    while(buf.hasRemaining()){
      buf.put((byte)0);
    }//end while loop
    //Set position to zero and return
    buf.position(0);
  }//end clearBufferData
  //---------------------------------//
  
  public static void main(
                        String[] args){
    System.out.println(
         "Demo write/read on channel");
    //Create and populate array
    byte[] array = {65,66,67,68,69,70};
    
    //Wrap array in a buffer
    ByteBuffer buf = 
                ByteBuffer.wrap(array);
    showBufferData(buf,"buf-raw data");
        
    //Get FileChannel for output
    try{
      FileChannel outCh = 
           (new FileOutputStream(
             "junk.txt")).getChannel();

      //Write output data
      System.out.println(
                   "Bytes written = " 
                   + outCh.write(buf));
      //Close channel
      outCh.close();

      //Clear buffer
      clearBufferData(buf,"buf");
      
      //Get FileChannel for input
      FileChannel inCh = 
           (new FileInputStream(
             "junk.txt")).getChannel();

      //Read and display data
      System.out.println(
                     "Bytes read = " 
                     + inCh.read(buf));
      //Close channel and display data
      inCh.close();
      showBufferData(buf,"buf");

    }catch(Exception e){
      System.out.println(e);}
  }// end main

}//end class Channel01 definition

Listing 16

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