Subclassing OutputStream
OutputStream is an abstract class that mainly describes the operations available with any OutputStream object. Specific subclasses know how to write bytes to particular destinations. For instance, a FileOutputStream uses native code to write data in files. A ByteArrayOutputStream uses pure Java to write its output in an expanding byte array.
Recall that there are three overloaded variants of the write( ) method in OutputStream, one abstract, two concrete:
public abstract void write(int b) throws IOException public void write(byte[] data) throws IOException public void write(byte[] data, int offset, int length) throws IOException
Subclasses must implement the abstract write(int b) method. They often also override the third variant, write(byte[], data int offset, int length), to improve performance. The implementation of the three-argument version of the write( ) method in OutputStream simply invokes write(int b) repeatedlythat is:
public void write(byte[] data, int offset, int length) throws IOException { for (int i = offset; i < offset+length; i++) write(data[i]); }
Most subclasses can provide a more efficient implementation of this method. The one-argument variant of write( ) merely invokes write(data, 0, data.length); if the three-argument variant has been overridden, this method will perform reasonably well. However, a few subclasses may override it anyway.
Example 2-3 is a simple program called NullOutputStream that mimics the behavior of /dev/null on Unix operating systems. Data written into a null output stream is lost.
Example 2-3. The NullOutputStream class
package com.elharo.io; import java.io.*; public class NullOutputStream extends OutputStream { private boolean closed = false; public void write(int b) throws IOException { if (closed) throw new IOException("Write to closed stream"); } public void write(byte[] data, int offset, int length) throws IOException { if (data == null) throw new NullPointerException("data is null"); if (closed) throw new IOException("Write to closed stream"); } public void close( ) { closed = true; } } |
The no-op flush( ) method inherited from the superclass is good enough here since this stream really doesn't need flushing. However, note that this class does need to check whether the stream is closed before writing anything, and check whether the array passed in to write( ) is null. Most subclasses will need to make similar checks.
By redirecting System.out and System.err to a null output stream in the shipping version of your program, you can disable any debugging messages that might have slipped through quality assurance. For example:
OutputStream out = new NullOutputStream( ); PrintStream ps = new PrintStream(out); System.setOut(ps); System.setErr(ps);