Here’s the class hierarchy to support random access files:

(Package java.io)

Object
    RandomAccessFile (implements DataInput, DataOutput)
DataInput
    ObjectInput
DataOutput
    ObjectOutput

Class RandomAccessFile

Instances of class RandomAccessFile support both reading and writing to a random access file. A random access file behaves like a large array of bytes stored in the file system. There is a kind of cursor, or index into the implied array, called the file pointer; input operations read bytes starting at the file pointer and advance the file pointer past the bytes read. If the random access file is created in read/write mode, then output operations are also available; output operations write bytes starting at the file pointer and advance the file pointer past the bytes written. Output operations that write past the current end of the implied array cause the array to be extended. The file pointer can be read by the getFilePointer method and set by the seek method.

It is generally true of all the reading routines in this class that if end-of-file is reached before the desired number of bytes has been read, an EOFException is thrown. If any byte cannot be read for any reason other than end-of-file, an IOException other than EOFException is thrown. In particular, an IOException may be thrown if the stream has been closed.

RandomAccessFile has two constructors:

ConstructorDescription
public RandomAccessFile(File file, String mode) throws IOExceptionCreates a random access file stream to read from, and optionally to write to, the file specified by the File argument.
public RandomAccessFile(String name, String mode) throws FileNotFoundExceptionCreates a random access file stream to read from, and optionally to write to, a file with the specified name.

It implements the DataInput and DataOutput interfaces, and so it supports all the methods required by them.

In addition, it implements the following methods:

MethodDescription
public void close() throws IOExceptionCloses this random access file stream and releases any system resources associated with the stream. A closed random access file cannot perform input or output operations and cannot be reopened.
public final FileDescriptor getFD() throws IOExceptionReturns the opaque file descriptor object associated with this stream.
public long getFilePointer() throws IOExceptionReturns the current offset in this file.
public long length() throws IOExceptionReturns the length of this file.
public void seek(long pos) throws IOExceptionSets the file-pointer offset, measured from the beginning of this file, at which the next read or write occurs. The offset may be set beyond the end of the file. Setting the offset beyond the end of the file does not change the file length. The file length will change only by writing after the offset has been set beyond the end of the file.
public void setLength(long newLength) throws IOExceptionSets the length of this file.If the present length of the file as returned by the length method is greater than the newLength argument then the file will be truncated. In this case, if the file offset as returned by the getFilePointer method is greater then newLength then after this method returns the offset will be equal to newLength.If the present length of the file as returned by the length method is smaller than the newLength argument then the file will be extended. In this case, the contents of the extended portion of the file are not defined.

Example

Here’s an example of using the RandomAccessFile class.

Employee

First, let’s create a class that represents an employee, whose data we’ll be saving in a random access file:

package randomAccess;

import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.RandomAccessFile;

/**
*   Class to represent an employee.
*
*   @author Bryan J. Higgs, 31 January, 2001
*/
public class Employee
{
    public static final int RECORD_SIZE = 4     // for the employeeNo
                                        + 80    // for the name (40 characters)
                                        + 8     // for the salary
                                        + 4;    // for the managedBy

    public static final int NO_MANAGER = -1;    // Means no managedBy

    /**
    *   Constructor (used for constructing an object and
    *   populating it using the read method),
    */
    public Employee()
    {
    }

    /**
    *   Constructor.
    */
    public Employee(int employeeID, String name, double salary, int managedBy)
    {
        m_employeeID = employeeID;
        m_name = name;
        m_salary = salary;
        m_managedBy = managedBy;
    }

    /**
    *   Returns the employee's ID.
    */
    public int getID()
    {
        return m_employeeID;
    }

    /**
    *   Returns the employee's name.
    */
    public String getName()
    {
        return m_name;
    }

    /**
    *   Returns the employee's salary.
    */
    public double getSalary()
    {
        return m_salary;
    }

    /**
    *   Returns the employee's manager's ID.
    */
    public int getManagedBy()
    {
        return m_managedBy;
    }

    /**
    *   Prints out the employee's information.
    */
    public void print()
    {
        PrintStream out = System.out;
        out.println("Employee ID: " + m_employeeID);
        out.println("       Name: " + m_name);
        out.println("     Salary: $" + m_salary);
        out.println(" Managed by: " + m_managedBy);
    }

    /**
    *   Writes employee data to the specified file.
    */
    public void write(RandomAccessFile file)
        throws IOException
    {
        file.writeInt(m_employeeID);
        writeName(file);
        file.writeDouble(m_salary);
        file.writeInt(m_managedBy);
    }

    /**
    *   Reads employee data from the specified file.
    */
    public void read(RandomAccessFile file)
        throws IOException
    {
        m_employeeID = file.readInt();
        m_name = readName(file);
        m_salary = file.readDouble();
        m_managedBy = file.readInt();
    }

    //// Private methods ////

    /**
    *   Writes the employee's name to the specified file.
    *   The name is truncated or padded to MAX_NAME_SIZE characters.
    */
    private void writeName(RandomAccessFile file)
        throws IOException
    {
        StringBuffer buf = new StringBuffer(m_name);
        buf.setLength(MAX_NAME_SIZE);   // Truncate or pad, as necessary
        file.writeChars(buf.toString());
    }

    /**
    *   Reads the employee's name from the specified file.
    *   The name is extracted, any padding removed, and then trimmed down.
    */
    private String readName(RandomAccessFile file)
        throws IOException
    {
        StringBuffer buf = new StringBuffer();
        for (int i = 0; i < MAX_NAME_SIZE; i++)
        {
            buf.append( file.readChar() );
        }
        String name = buf.toString().replace('\0', ' ').trim();
        return name;
    }

    ////// Private data ///////
    private static final int MAX_NAME_SIZE = 40;    // Characters

    private int     m_employeeID = -1;   // Employee id
    private String  m_name = "";         // Employee name
    private double  m_salary = 0.0;      // Employee salary
    private int     m_managedBy = -1;    // Manager's employee id (-1 if no manager)
}

EmployeeDataLoader

And here’s a class that loads employee data into a random access file:

package randomAccess;

import java.io.IOException;
import java.io.RandomAccessFile;

/**
*   Class to load a set of employee data to a file.
*
*   @author Bryan J. Higgs, 31 January, 2001.
*/
public class EmployeeDataLoader
{
    /**
    *   Main entry point.
    *
    *   @param args command line arguments.
    *               args[0] is the name of the file to load.
    */
    public static void main(String[] args)
    {
        if (args.length < 1)
        {
            System.err.println(
                "Usage: java randomAccess.EmployeeDataLoader <file-to-load>");
            return;
        }
        // Extract the filename
        String fileName = args[0];

        // Construct all employees (empID, empName, empSalary, managedBy)
        Employee bigBoss = new Employee(1, "Richie Rich", 2000000.0, -1);
        Employee cfo     = new Employee(2, "Guy Noire", 200000.0, 1);
        Employee hrBoss  = new Employee(3, "Prescott Hireling", 100000.0, 1);
        Employee sectry  = new Employee(4, "Dottie Magnusson", 40000.0, 3);
        Employee opsBoss = new Employee(5, "Frank Getitdone", 120000.0, 1);
        Employee minion  = new Employee(6, "Joe Duzit", 30000.0, 5);

        RandomAccessFile file = null;
        try
        {
            // Create random access file object for writing.
            file = new RandomAccessFile(fileName, "rw");
            // Write all employees to the file
            // NOTE: The employee ID is the index of the employee data,
            //       so the employees are written in employee ID order.
            bigBoss.write(file);
            cfo.write(file);
            hrBoss.write(file);
            sectry.write(file);
            opsBoss.write(file);
            minion.write(file);
        }
        catch (IOException ex)
        {
            ex.printStackTrace();
        }
        finally
        {
            // Close the file, under all circumstances.
            if (file != null)
            {
                try
                {
                    file.close();
                }
                catch (IOException ex)
                {
                    // Ignore
                }
            }
        }
    }
}

EmployeeDataReader

Now, here’s a class that reads the data back in from the file, reading employees sequentially:

package randomAccess;

import java.io.EOFException;
import java.io.IOException;
import java.io.RandomAccessFile;

/**
*   Class to read a set of employee data from a file.
*
*   @author Bryan J. Higgs, 31 January, 2001.
*/
public class EmployeeDataReader
{
    /**
    *   Main entry point.
    *
    *   @param args command line arguments.
    *               args[0] is the name of the file to read.
    */
    public static void main(String[] args)
    {
        if (args.length < 1)
        {
            System.err.println(
                "Usage: java randomAccess.EmployeeDataReader <file-to-read>");
            return;
        }
        // Extract the filename
        String fileName = args[0];

        RandomAccessFile file = null;
        try
        {
            // Create random access file object for reading
            file = new RandomAccessFile(fileName, "r");

            // Read the employees, in sequential order.
            while (true)
            {
                Employee emp = new Employee();
                emp.read(file);
                emp.print();
            }
        }
        catch (EOFException ex)
        {
            // Do nothing;  there are no more records to read
        }
        catch (IOException ex)
        {
            ex.printStackTrace();
        }
        finally
        {
            // Close file, under all circumstances.
            if (file != null)
            {
                try
                {
                    file.close();
                }
                catch (IOException ex)
                {
                    // Ignore
                }
            }
        }
    }
}

which produces the following output:

Employee ID: 1
       Name: Richie Rich
     Salary: $2000000.0
 Managed by: -1
Employee ID: 2
       Name: Guy Noire
     Salary: $200000.0
 Managed by: 1
Employee ID: 3
       Name: Prescott Hireling
     Salary: $100000.0
 Managed by: 1
Employee ID: 4
       Name: Dottie Magnusson
     Salary: $40000.0
 Managed by: 3
Employee ID: 5
       Name: Frank Getitdone
     Salary: $120000.0
 Managed by: 1
Employee ID: 6
       Name: Joe Duzit
     Salary: $30000.0
 Managed by: 5

EmployeeTree

Finally, here’s how we can do random access to the file.

We now read the file using random access, to build a simple ‘org chart’ of the employees:

package randomAccess;

import java.io.EOFException;
import java.io.IOException;
import java.io.RandomAccessFile;

/**
*   Class to print out a management hierarchy, given an employee ID.
*
*   @author Bryan J. Higgs, 31 January, 2001.
*/
public class EmployeeTree
{
    /**
    *   Main entry point.
    *
    *   @param args command line arguments.
    *               args[0] is the name of the file to read.
    *               args[1] is the employee ID.
    */
    public static void main(String[] args)
    {
        if (args.length < 2)
        {
            System.err.println(
                "Usage: java randomAccess.EmployeeTree <file-to-read> <empID>");
            return;
        }
        // Extract the filename
        String fileName = args[0];
        // Extract the employee ID
        int empID = Integer.parseInt(args[1]);

        RandomAccessFile file = null;
        try
        {
            // Create random access file object for reading
            file = new RandomAccessFile(fileName, "r");
            // Print the management hierarchy
            printTree(file, empID);
            // end it with a fresh newline
            System.out.println();
        }
        catch (IOException ex)
        {
            ex.printStackTrace();
        }
        finally
        {
            if (file != null)
            {
                try
                {
                    file.close();
                }
                catch (IOException ex)
                {
                    // Ignore
                }
            }
        }
    }

    /**
    *   Prints out a management hierarchy, given an employee ID
    */
    private static int printTree(RandomAccessFile file, int empID)
        throws IOException
    {
        // Calculate position of employee record in file.
        long position = (empID-1) * Employee.RECORD_SIZE;
        // Seek to it.
        file.seek(position);
        // Read employee data
        Employee emp = new Employee();
        emp.read(file);
        // Perform a sanity check
        if (emp.getID() != empID)
        {
            throw new IllegalStateException(
                            "Employee IDs don't match:" + empID + ", " + emp.getID());
        }

        int level = 0;  // Used for indentation
        if (emp.getManagedBy() != Employee.NO_MANAGER)
        {
            // If employee has a manager, recurse
            level = printTree(file, emp.getManagedBy());
            // Indent to level
            for (int i = 0; i < level; i++)
                System.out.print(' ');
            System.out.print("manages ");
        }
        // Print out employee name
        System.out.println(emp.getName());

        return ++level;
    }
}

which produces the following output:

Richie Rich
 manages Frank Getitdone
  manages Joe Duzit