| Input and Output Streams

## Writing Filters for Random Access Files

> Let's rewrite the example from Writing Your Own Filtered 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 CheckedIOTest example from Writing Your Own Filtered Streams implements two filtered streams, CheckedInputStream and CheckedOutputStream, that compute a checksum as data is read from or written to the stream.

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

> #### CheckedDataOutput vs. CheckedOutputStream

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

> > The first difference between CheckedDataOutput and CheckedOutputStream is that CheckedDataOutput does not extend FilterOutputStream. Instead, it implements the DataOutput interface.

> > >     public class CheckedDataOutput implements DataOutput

> > Note: In the interest of keeping the example simple, the CheckedDataOutput class actually provided in this lesson is not declared to implement DataOutput. This is 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 is different from CheckedOutputStream's constructor: CheckedDataOutput is created on a DataOutput object rather than on an OutputStream.

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

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

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

> #### CheckedDataInput vs. CheckedInputStream

> > CheckedDataInput requires the same changes as CheckedDataOuput:
> > - CheckedDataInput does not derive from FilterInputStream but implements the DataInput interface instead.

> >   Note: In the interest of keeping the example simple, the CheckedDataInput class actually provided in this lesson is not declared to implement DataInput. This is because the DataInput interface specifies so many methods. However, the CheckedDataInput class as provided in the example does implement several of DataInput's methods to illustrate how it should work.

> > - 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 as well. 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);
> > >     }

> #### The Main Programs

> > Finally, this example has two main programs to test the new filters: CheckedDITest, which runs the filters on sequential access files (DataInputStream and DataOutputStream objects), and CheckedRAFTest, which runs the filters on random access files (RandomAccessFiles).

> > These two main programs differ only in the type of object they open the checksum filters on. CheckedDITest creates a DataInputStream and a DataOutputStream and uses the checksum filter on those. Like this:

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

> > CheckedRAFTest creates two RandomAccessFiles, one for reading and one for writing, and uses the checksum filter on those:

> > >     cis = new CheckedDataInput(new RandomAccessFile("farrago.txt", "r"), inChecker);
> > >     cos = 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

> > #### See Also

> > > java.io.DataInput
> > > java.io.DataOutput

> | Input and Output Streams