Java 1.1 IO streams | CodeGuru

Java 1.1 IO streams

Bruce Eckel’s Thinking in Java Contents | Prev | Next At this point you might be scratching your head, wondering if there is another design for IO streams that could require more typing. Could someone have come up with an odder design?” Prepare yourself: Java 1.1 makes some significant modifications to the IO stream library. […]

Written By
CodeGuru Staff
CodeGuru Staff
Mar 1, 2001
7 minute read
CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More

At


this point you might be scratching your head, wondering if there is another


design for IO streams that could require


more

typing. Could someone have come up with an odder design?” Prepare


yourself: Java 1.1 makes some significant modifications to the IO stream


library. When you see the

Reader
and
Writer
classes your first thought (like mine) might be that these were meant to
replace the
InputStream
and
OutputStream
classes. But that’s not the case. Although some aspects of the original
streams library are deprecated (if you use them you will receive a warning from
the compiler), the old streams have been left in for backwards compatibility and:
  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.

The


most important reason for adding the


Reader

and


Writer

hierarchies in Java 1.1


is for
internationalization.
The old IO stream hierarchy supports only 8-bit byte streams and doesn’t
handle the 16-bit Unicode characters well. Since Unicode is used for
internationalization (and Java’s native
char
is
16-bit
Unicode),
the
Reader
and
Writer
hierarchies were added to support Unicode in all IO operations. In addition,
the new libraries are designed for faster operations than the old.

As


is the practice in this book, I will attempt to provide an overview of the


classes but assume that you will use online documentation to determine all the


details, such as the exhaustive list of methods.


Sources
and sinks of data

Almost


all of the Java 1.0


IO stream classes have corresponding Java 1.1

classes to provide native Unicode manipulation. It would be easiest to say
“Always use the new classes, never use the old ones,” but things
are not that simple. Sometimes you are forced into using the Java 1.0 IO stream
classes because of the library design; in particular, the
java.util.zip
libraries are new additions to the old stream library and they rely on old
stream components. So the most sensible approach to take is to
try
to use the
Reader
and
Writer
classes whenever you can, and you’ll discover the situations when you
have to drop back into the old libraries because your code won’t compile.

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

In


general, you’ll find that the interfaces in the old library components


and the new ones are similar if not identical.


Modifying
stream behavior

In


Java 1.0

,
streams were adapted for particular needs using “decorator”
subclasses of
FilterInputStream
and
FilterOutputStream.
Java 1.1

IO streams continues the use of this idea, but the model of deriving all of the
decorators from the same “filter” base class is not followed. This
can make it a bit confusing if you’re trying to understand it by looking
at the class hierarchy.

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

Corresponding
Java 1.1
class

FilterInputStream FilterReader
FilterOutputStream FilterWriter
(
abstract
class with no subclasses)
BufferedInputStream BufferedReader

(also
has readLine( ))

BufferedOutputStream BufferedWriter
DataInputStream use
DataInputStream

(Except
when you need to use
readLine( ),
when you should use a
BufferedReader)

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.

To


make the transition to using a


PrintWriter

easier, it has constructors that take any


OutputStream

object. However,


PrintWriter

has no more support for formatting than


PrintStream

does; the interfaces are virtually the same.


Advertisement

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:

Java
1.0
classes without corresponding Java 1.1

classes

DataOutputStream
File
RandomAccessFile
SequenceInputStream

The


DataOutputStream

,


in particular, is used without change, so for storing and retrieving data in a


transportable format you’re forced to stay in the


InputStream

and


OutputStream

hierarchies.


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


3 shows a bug in the design of the new IO stream library. If you have a


String

and you want to read from it, you’re


not

supposed to use a


StringBufferInputStream

any more. When you compile code involving a


StringBufferInputStream

constructor, you get a deprecation message telling you to not use it. Instead,


you’re supposed to use a


StringReader

.


However, if you want to do formatted memory input as in section 3, you’re


forced to use a


DataInputStream

– there is no “DataReader” to replace it – and a


DataInputStream

constructor requires an


InputStream

argument. So you have no choice but to use the deprecated


StringBufferInputStream

class. The compiler will give you a deprecation message but there’s


nothing you can do about it.


[48]

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

Java


1.1


has added methods in class
System
that allow you to redirect the standard input, output, and error IO streams
using simple static method calls:
setIn(InputStream)

setOut(PrintStream)

setErr(PrintStream)

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.

However,


both


System.setOut( )

and


System.setErr( )

require a


PrintStream

object as an argument, so you are forced to call the


PrintStream

constructor. You might wonder, if Java 1.1


deprecates the entire
PrintStream
class by deprecating the constructor, why the library designers, at the same
time as they added this deprecation, also add new methods to
System
that required a
PrintStream
rather than a
PrintWriter,
which is the new and preferred replacement. It’s a mystery.

[48]

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

Contents

|

Prev

|

Next
CodeGuru Logo

CodeGuru covers topics related to Microsoft-related software development, mobile development, database management, and web application programming. In addition to tutorials and how-tos that teach programmers how to code in Microsoft-related languages and frameworks like C# and .Net, we also publish articles on software development tools, the latest in developer news, and advice for project managers. Cloud services such as Microsoft Azure and database options including SQL Server and MSSQL are also frequently covered.

Property of TechnologyAdvice. © 2026 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.