|
|
Input and Output Streams |
The following is a list of steps to take when writing your own filtered input and output streams:This section shows you how to implement your own filtered streams through an example that implements a matched pair of filtered input and output streams. Many thanks to David Connelly from the Java team for providing us with this example.
- Create a subclass of
FilterInputStreamandFilterOutputStream. Input and output streams often come in pairs, so it's likely that you will need to create both input and output versions of your filter stream.- Override the
readandwritemethods.- Override any other methods that you might need.
- Make sure the input and output streams work together.
Both the input and the output stream use a checksum class to compute a checksum on the data written to or read from the stream. The checksum is used to determine whether the data read by the input stream matches that written by the output stream.
Four classes and one interface make up this example program:
- The filtered input and output stream subclasses--
CheckedOutputStreamandCheckedInputStream.- The
Checksuminterface and theAdler32class compute a checksum for the streams.- The
CheckedIOTestclass defines themainmethod for the program.The
CheckedOutputStreamClassTheCheckedOutputStreamclass is a subclass ofFilterOutputStreamthat computes a checksum on data as it's being written to the stream. When creating aCheckedOutputStream, you must use its only constructor:This constructor takes anpublic CheckedOutputStream(OutputStream out, Checksum cksum) { super(out); this.cksum = cksum; }OutputStreamargument and aChecksumargument. TheOutputStreamargument is the output stream that thisCheckedOutputStreamshould filter. TheChecksumargument is an object that can compute a checksum.CheckedOutputStreaminitializes itself by calling its superclass constructor and initializing a private variable,cksum, with theChecksumobject. TheCheckedOutputStreamusescksumto update the checksum each time data is written to the stream.
CheckedOutputStreamneeds to overrideFilterOutputStream'swritemethods so that each time thewritemethod is called, the checksum is updated.FilterOutputStreamdefines three versions of thewritemethod:
write(int i)write(byte[] b)write(byte[] b, int offset, int length)CheckedOutputStreamoverrides all three of these methods:The implementations of these threepublic void write(int b) throws IOException { out.write(b); cksum.update(b); } public void write(byte[] b) throws IOException { out.write(b, 0, b.length); cksum.update(b, 0, b.length); } public void write(byte[] b, int off, int len) throws IOException { out.write(b, off, len); cksum.update(b, off, len); }writemethods are straightforward: Write the data to the output stream that this filtered stream is attached to, then update the checksum.The
CheckedInputStreamClassTheCheckedInputStreamclass is very similar to theCheckedOutputStreamclass.CheckedInputStreamis a subclass ofFilterInputStreamthat computes a checksum on data as it's being read from the stream. When creating aCheckedInputStream, you must use its only constructor:This constructor is similar to the constructor forpublic CheckedInputStream(InputStream in, Checksum cksum) { super(in); this.cksum = cksum; }CheckedOutputStream.Just as
CheckedOutputStreamneeded to overrideFilterOutputStream'swritemethods,CheckedInputStreammust overrideFilterInputStream'sreadmethods so that each time thereadmethod is called, the checksum is updated. As withFilterOutputStream,FilterInputStreamdefines three versions of thereadmethod andCheckedInputStreamoverride all of them:The implementations of these threepublic int read() throws IOException { int b = in.read(); if (b != -1) { cksum.update(b); } return b; } public int read(byte[] b) throws IOException { int len; len = in.read(b, 0, b.length); if (len != -1) { cksum.update(b, 0, b.length); } return len; } public int read(byte[] b, int off, int len) throws IOException { len = in.read(b, off, len); if (len != -1) { cksum.update(b, off, len); } return len; }readmethods are straightforward: Read the data from the input stream that this filtered stream is attached to, then if any data was actually read, update the checksum.The
ChecksumInterface and theAdler32ClassTheChecksuminterface defines four methods for checksum objects to implement; these methods reset, update, and return the checksum value. You could write aChecksumclass that computes a specific type of checksum such as the CRC-32 checksum. Note that inherent in the checksum is the notion of state. The checksum object doesn't just compute a checksum in one go. Rather, the checksum is updated each time information is read from or written to the stream for which this object computes a checksum. If you want to reuse a checksum object, you must reset it.For this example, we implemented the
Adler32checksum, which is almost as reliable as a CRC-32 checksum but can be computed much faster.A Program for Testing
The last class in the example, CheckedIOTest, contains themainmethod for the program.Theimport java.io.*; class CheckedIOTest { public static void main(String[] args) { Adler32 inChecker = new Adler32(); Adler32 outChecker = new Adler32(); CheckedInputStream cis = null; CheckedOutputStream cos = null; try { cis = new CheckedInputStream(new FileInputStream("farrago.txt"), inChecker); cos = new CheckedOutputStream(new FileOutputStream("outagain.txt"), outChecker); } catch (FileNotFoundException e) { System.err.println("CheckedIOTest: " + e); System.exit(-1); } catch (IOException e) { System.err.println("CheckedIOTest: " + e); System.exit(-1); } try { int c; while ((c = cis.read()) != -1) { cos.write(c); } System.out.println("Input stream check sum: " + inChecker.getValue()); System.out.println("Output stream check sum: " + outChecker.getValue()); cis.close(); cos.close(); } catch (IOException e) { System.err.println("CheckedIOTest: " + e); } } }mainmethod creates twoAdler32checksum objects, one each for aCheckedOutputStreamand aCheckedInputStream. The example requires two checksum objects because the checksum objects are updated during calls toreadandwriteand those calls are occurring concurrently.Next,
mainopens aCheckedInputStreamon a small text file,farrago.txt, and aCheckedOutputStreamon an output file namedoutagain.txt, which doesn't exist until you run the program for the first time.The
mainmethod reads the text from theCheckedInputStreamand simply copies it to theCheckedOutputStream. Thereadandwritemethods use theAdler32checksum objects to compute a checksum during reading and writing. After the input file has been completely read (and consequently the output file has been completely written), the program prints out the checksum for both the input and output streams (which should match) and then closes them both.When you run
CheckedIOTest, you should see this output:Input stream check sum: 736868089 Output stream check sum: 736868089Filtering Random Access Files
The filtered streams injava.ioall inherit fromInputStreamorOutputStream, which implement sequential access files. So if you subclassFilterInputStreamorFilterOutputStreamyour filtered streams will also be sequential access files. Writing Filters for Random Access Files later in this lesson shows you how to re-write this example so that it works on aRandomAccessFileas well as on aDataInputStreamor aDataOutputStream.
|
|
Input and Output Streams |