The JavaTM Tutorial

| Start of Tutorial > Start of Trail > Start of Lesson | Search Feedback Form

Trail: Essential Java Classes
Lesson: I/O: Reading and Writing (but no 'rithmetic)

## Writing Filters for Random Access Files

> Let's rewrite the example from How to Write Your Own Filter Streams so that it works on `RandomAccessFiles`. Because `RandomAccessFile` implements the `DataInput` and `DataOutput` interfaces, a side benefit is that the filtered stream will also work with other `DataInput` and `DataOutput` streams including some sequential access streams such as `DataInputStream` and `DataOutputStream`.

> The example `CheckedIODemo` from How to Write Your Own Filter Streams implements two filter streams that compute a checksum as data is read from or written to the stream. Those streams are `CheckedInputStream` and `CheckedOutputStream`.

> In the new example, `CheckedDataOutput` is a rewrite of `CheckedOutputStream`--it computes a checksum for data writen to the stream. However, it operates on `DataOutput` objects instead of on `OutputStream` objects. Similarly, `CheckedDataInput` modifies `CheckedInputStream` so that it now works on `DataInput` objects instead of `InputStream` objects.

### `CheckedDataOutput` versus `CheckedOutputStream`

> Let's look at how `CheckedDataOutput` differs from `CheckedOutputStream`.

> The first difference in these two classes is that `CheckedDataOutput` does not extend `FilterOutputStream`. Instead, it implements the `DataOutput` interface.

> >     public class CheckedDataOutput implements DataOutput

> > Note: To keep the example code simple, the `CheckedDataOutput` class actually provided in this lesson is not declared to implement `DataOutput`, because the `DataOutput` interface specifies so many methods. However, the `CheckedDataOutput` class as provided in the example does implement several of `DataOutput`'s methods to illustrate how it should work.

> Next, `CheckedDataOutput` declares a private variable to hold a `DataOutput` object.

> >     private DataOutput out;

> This is the object to which data will be written.

> The constructor for `CheckedDataOutput` differs from `CheckedOutputStream`'s constructor in that `CheckedDataOutput` is created on a `DataOutput` object rather than on an `OutputStream`.

> >     public CheckedDataOutput(DataOutput out, Checksum cksum) {
> >         this.cksum = cksum;
> >         this.out = out;
> >     }

> This constructor does not call `super(out)` like the `CheckedOutputStream` constructor did, because `CheckedDataOutput` extends from `Object` rather than from a stream class.

> Those are the only modifications made to `CheckedOutputStream` to create a filter that works on `DataOutput` objects.

### `CheckedDataInput` versus `CheckedInputStream`

> `CheckedDataInput` requires the same changes as `CheckedDataOuput`, as follows:
> - `CheckedDataInput` does not derive from `FilterInputStream`. Instead, it implements the `DataInput` interface instead.
> - `CheckedDataInput` declares a private variable to hold a `DataInput` object, which it wraps.
> - The constructor for `CheckedDataInput` requires a `DataInput` object rather than an `InputStream`.
> In addition to these changes, the `read` methods are changed. `CheckedInputStream` from the original example implements two `read` methods, one for reading a single byte and one for reading a byte array. The `DataInput` interface has methods that implement the same functionality, but they have different names and different method signatures. Thus, the `read` methods in the `CheckedDataInput` class have new names and method signatures:

> >     public byte readByte() throws IOException {
> >         byte b = in.readByte();
> >         cksum.update(b);
> >         return b;
> >     }

> >     public void readFully(byte[] b) throws IOException {
> >         in.readFully(b, 0, b.length);
> >         cksum.update(b, 0, b.length);
> >     }

> >     public void readFully(byte[] b, int off, int len) throws IOException {
> >         in.readFully(b, off, len);
> >         cksum.update(b, off, len);
> >     }

> Also, the `DataInput` interface declares many other methods that we don’t implement for this example.

### The Main Programs

> Finally, this example has two main programs to test the new filters:
> - `CheckedDIDemo`, which runs the filters on sequential access files (`DataInputStream` and `DataOutputStream` objects)
> - `CheckedRAFDemo`, which runs the filters on random access files (`RandomAccessFiles` objects)
> These two main programs differ only in the type of object they open the checksum filters on. `CheckedDIDemo` creates a `DataInputStream` and a `DataOutputStream` and uses the checksum filter on them, as in the following code:

> >     in = new CheckedDataInput(new DataInputStream(
> >              new FileInputStream("farrago.txt")), inChecker);
> >     out = new CheckedDataOutput(new DataOutputStream(
> >               new FileOutputStream("outagain.txt")), outChecker);

> `CheckedRAFDemo` creates two `RandomAccessFile` objects: one for reading and one for writing. It uses the checksum filter on them as follows:

> >     in = new CheckedDataInput(
> >              new RandomAccessFile("farrago.txt", "r"), inChecker);
> >     out = new CheckedDataOutput(
> >               new RandomAccessFile("outagain.txt", "rw"), outChecker);

> When you run either of these programs you should see the following output:

> >     Input stream check sum: 736868089
> >     Output stream check sum: 736868089

| Start of Tutorial > Start of Trail > Start of Lesson | Search Feedback Form

Copyright 1995-2004 Sun Microsystems, Inc. All rights reserved.