Java 1.1 IO streams

Bruce Eckel’s Thinking in Java Contents | Prev | Next

  1. New
    classes have been put into the old hierarchy, so it’s obvious that Sun is
    not abandoning the old streams.
  2. There
    are times when you’re supposed to use classes in the old hierarchy
    in
    combination

    with classes in the new hierarchy and to accomplish this there are
    “bridge” classes:
    InputStreamReader
    converts an
    InputStream
    to
    a
    Reader
    and
    OutputStreamWriter
    converts an
    OutputStream
    to a
    Writer.
As
a result there are situations in which you have
more
layers of wrapping with the new IO stream library than with the old. Again,
this is a drawback of the decorator pattern – the price you pay for added
flexibility.

Sources
and sinks of data

Here
is a table that shows the correspondence between the sources and sinks of
information (that is, where the data physically comes from or goes to) in the
old and new libraries.

Sources
& Sinks:

Java
1.0
class

Corresponding
Java 1.1
class

InputStream

Reader

converter:
InputStreamReader

OutputStream

Writer

converter:
OutputStreamWriter

FileInputStream

FileReader

FileOutputStream

FileWriter

StringBufferInputStream

StringReader

(no
corresponding class)

StringWriter

ByteArrayInputStream

CharArrayReader

ByteArrayOutputStream

CharArrayWriter

PipedInputStream

PipedReader

PipedOutputStream

PipedWriter

Modifying
stream behavior

In
the following table, the correspondence is a rougher approximation than in the
previous table. The difference is because of the class organization: while
BufferedOutputStream
is a subclass of
FilterOutputStream,
BufferedWriter
is
not
a subclass of
FilterWriter
(which, even though it is
abstract,
has no subclasses and so appears to have been put in either as a placeholder or
simply so you wouldn’t wonder where it was). However, the interfaces to
the classes are quite a close match and it’s apparent that you’re
supposed to use the new versions instead of the old whenever possible (that is,
except in cases where you’re forced to produce a
Stream
instead of a
Reader
or
Writer).

Filters:

Java
1.0
class

FilterInputStream

FilterReader

FilterOutputStream

FilterWriter
(
abstract
class with no subclasses)

BufferedInputStream

BufferedReader

(also
has readLine( ))

BufferedOutputStream

BufferedWriter

DataInputStream

PrintStream

PrintWriter

LineNumberInputStream

LineNumberReader

StreamTokenizer

StreamTokenizer

(use
constructor that takes a
Reader
instead)

PushBackInputStream

PushBackReader

There’s
one direction that’s quite clear: Whenever you want to use
readLine( ),
you shouldn’t do it with a
DataInputStream
any more (this is met with a deprecation message at compile time), but instead
use a
BufferedReader.
Other than this,
DataInputStream
is still a “preferred” member of the Java 1.1 IO library.

Unchanged
Classes

Apparently,
the Java library designers felt that they got some of the classes right the
first time so there were no changes to these and you can go on using them as
they are:

DataOutputStream

File

RandomAccessFile

SequenceInputStream

An
example

To
see the effect of the new classes, let’s look at the appropriate portion
of the
IOStreamDemo.java
example modified to use the
Reader
and
Writer
classes:

//: NewIODemo.java
// Java 1.1 IO typical usage
import java.io.*;
 
public class NewIODemo {
  public static void main(String[] args) {
    try {
      // 1. Reading input by lines:
      BufferedReader in =
        new BufferedReader(
          new FileReader(args[0]));
      String s, s2 = new String();
      while((s = in.readLine())!= null)
        s2 += s + "n";
      in.close();
 
      // 1b. Reading standard input:
      BufferedReader stdin =
        new BufferedReader(
          new InputStreamReader(System.in));
      System.out.print("Enter a line:");
      System.out.println(stdin.readLine());
 
      // 2. Input from memory
      StringReader in2 = new StringReader(s2);
      int c;
      while((c = in2.read()) != -1)
        System.out.print((char)c);
 
      // 3. Formatted memory input
      try {
        DataInputStream in3 =
          new DataInputStream(
            // Oops: must use deprecated class:
            new StringBufferInputStream(s2));
        while(true)
          System.out.print((char)in3.readByte());
      } catch(EOFException e) {
        System.out.println("End of stream");
      }
 
      // 4. Line numbering & file output
      try {
        LineNumberReader li =
          new LineNumberReader(
            new StringReader(s2));
        BufferedReader in4 =
          new BufferedReader(li);
        PrintWriter out1 =
          new PrintWriter(
            new BufferedWriter(
              new FileWriter("IODemo.out")));
        while((s = in4.readLine()) != null )
          out1.println(
            "Line " + li.getLineNumber() + s);
        out1.close();
      } catch(EOFException e) {
        System.out.println("End of stream");
      }
 
      // 5. Storing & recovering data
      try {
        DataOutputStream out2 =
          new DataOutputStream(
            new BufferedOutputStream(
              new FileOutputStream("Data.txt")));
        out2.writeDouble(3.14159);
        out2.writeBytes("That was pi");
        out2.close();
        DataInputStream in5 =
          new DataInputStream(
            new BufferedInputStream(
              new FileInputStream("Data.txt")));
        BufferedReader in5br =
          new BufferedReader(
            new InputStreamReader(in5));
        // Must use DataInputStream for data:
        System.out.println(in5.readDouble());
        // Can now use the "proper" readLine():
        System.out.println(in5br.readLine());
      } catch(EOFException e) {
        System.out.println("End of stream");
      }
 
      // 6. Reading and writing random access
      // files is the same as before.
      // (not repeated here)
 
    } catch(FileNotFoundException e) {
      System.out.println(
        "File Not Found:" + args[1]);
    } catch(IOException e) {
      System.out.println("IO Exception");
    }
  }
} ///:~ 

In
general, you’ll see that the conversion is fairly straightforward and the
code looks quite similar. There are some important differences, though. First
of all, since random access files have not changed, section 6 is not repeated.

Section
1 shrinks a bit because if all you’re doing is reading line input you
need only to wrap a
BufferedReader
around a
FileReader.
Section 1b shows the new way to wrap System.in
for reading
console
input, and this expands because
System.in
is a
DataInputStream
and
BufferedReader
needs a
Reader
argument, so
InputStreamReader
is brought in to perform the translation.

In
section 2 you can see that if you have a
String
and want to read from it you just use a
StringReader
instead of a
StringBufferInputStream
and the rest of the code is identical.

Section
4 is a reasonably straightforward translation from the old streams to the new,
with no surprises. In section 5, you’re forced to use all the old streams
classes because
DataOutputStream
and
DataInputStream
require them and there are no alternatives. However, you don’t get any
deprecation messages at compile time. If a stream is deprecated, typically its
constructor produces a deprecation message to prevent you from using the entire
class, but in the case of
DataInputStream
only the
readLine( )
method is deprecated since you’re supposed to use a
BufferedReader
for
readLine( )
(but a
DataInputStream
for all other formatted input).

If
you compare section 5 with that section in
IOStreamDemo.java,
you’ll notice that in
this
version, the data is written
before
the text. That’s because a bug was introduced in Java 1.1,
which is shown in the following code:

//: IOBug.java
// Java 1.1 (and higher?) IO Bug
import java.io.*;
 
public class IOBug {
  public static void main(String[] args)
  throws Exception {
    DataOutputStream out =
      new DataOutputStream(
        new BufferedOutputStream(
          new FileOutputStream("Data.txt")));
    out.writeDouble(3.14159);
    out.writeBytes("That was the value of pin");
    out.writeBytes("This is pi/2:n");
    out.writeDouble(3.14159/2);
    out.close();
 
    DataInputStream in =
      new DataInputStream(
        new BufferedInputStream(
          new FileInputStream("Data.txt")));
    BufferedReader inbr =
      new BufferedReader(
        new InputStreamReader(in));
    // The doubles written BEFORE the line of text
    // read back correctly:
    System.out.println(in.readDouble());
    // Read the lines of text:
    System.out.println(inbr.readLine());
    System.out.println(inbr.readLine());
    // Trying to read the doubles after the line
    // produces an end-of-file exception:
    System.out.println(in.readDouble());
  }
} ///:~ 

It
appears that anything you write after a call to
writeBytes( )
is not recoverable. This is a rather limiting bug, and we can hope that it will
be fixed by the time you read this. You should run the above program to test
it; if you don’t get an exception and the values print correctly then
you’re out of the woods.

Redirecting
standard IO

Redirecting
output is especially useful if you suddenly start creating a large amount of
output on your screen and it’s scrolling past faster than you can read
it. Redirecting input is valuable for a command-line program in which you want
to test a particular user-input sequence repeatedly. Here’s a simple
example that shows the use of these methods:

//: Redirecting.java
// Demonstrates the use of redirection for 
// standard IO in Java 1.1
import java.io.*;
 
class Redirecting {
  public static void main(String[] args) {
    try {
      BufferedInputStream in =
        new BufferedInputStream(
          new FileInputStream(
            "Redirecting.java"));
      // Produces deprecation message:
      PrintStream out =
        new PrintStream(
          new BufferedOutputStream(
            new FileOutputStream("test.out")));
      System.setIn(in);
      System.setOut(out);
      System.setErr(out);
 
      BufferedReader br =
        new BufferedReader(
          new InputStreamReader(System.in));
      String s;
      while((s = br.readLine()) != null)
        System.out.println(s);
      out.close(); // Remember this!
    } catch(IOException e) {
      e.printStackTrace();
    }
  }
} ///:~ 

This
program attaches standard input to a file, and redirects standard output and
standard error to another file.

This
is another example in which a deprecation message is inevitable. The message
you can get when compiling with the
-deprecation
flag
is:

Note:
The constructor java.io.PrintStream(java.io.OutputStream)

has
been deprecated.


[48]
Perhaps by the time you read this, the bug will be fixed.

More by Author

Must Read