The Java TM Tutorial

| Start of Tutorial > Start of Trail > Start of Lesson | Search

Trail: To 1.1 -- And Beyond!
Lesson: Migrating to 1.1

## How to Convert Code that Uses I/O

> To support internationalization, the JDK 1.1 release adds character streams to the `java.io` package. In addition, a few of the package's classes and methods have been deprecated. Converting a 1.0 program that uses `java.io` API usually requires two steps:
> 1. If the program uses a byte stream to read or write data, decide whether the byte stream is still appropriate for the data being read or written by the program. If not, convert the program so that it uses a character stream instead.
> 2. Make sure that the program does not use any deprecated API.
> The following two sections provide details and examples to help you with these two steps.

### Step 1: If Appropriate, Switch from Byte Streams to Character Streams

> Does the program use a byte stream (an object that inherits from `InputStream` or `OutputStream`)? If so, does the stream truly contain byte data, or does it contain characters? If the stream contains characters, then you should seriously consider changing the program to use character streams, which were introduced in 1.1.

> Character streams act like byte streams except that they contain 16-bit Unicode characters rather than 8-bit bytes. With character streams, you can write programs that don't depend upon a specific character encoding, and are therefore easy to internationalize.

> Another advantage of character streams is that they are potentially much more efficient than byte streams. The implementations of many of Java's original byte streams are oriented around byte-at-a-time read and write operations. The character-stream classes, in contrast, are oriented around buffer-at-a-time read and write operations. This difference, in combination with a more efficient locking scheme, allows the character stream classes to make up for the added overhead of encoding conversion in many cases.

> Character streams are implemented by the `java.io` `Reader` and `Writer` classes and their subclasses. The character-stream classes support essentially the same operations as the byte-stream classes, except that where byte-stream methods operate on bytes or byte arrays, character-stream methods operate on characters, character arrays, or strings.

> Although the Java language stores strings in Unicode, typical user-readable text files use encodings that are not necessarily related to Unicode, or even to ASCII. Character streams hide the complexity of dealing with these encodings by providing two classes that serve as bridges between byte streams and character streams. The `InputStreamReader` class implements a character-input stream that reads bytes from a byte-input stream and converts them to characters according to a specified encoding. Similarly, the `OutputStreamWriter` class implements a character-output stream that converts characters into bytes according a specified encoding and writes them to a byte-output stream.

> Most of the functionality available for byte streams is also provided for character streams. This is reflected in the name of each character-stream class, whose prefix is usually shared with the name of the corresponding byte-stream class. For example, there is a `PushbackReader` class that provides the same functionality for character streams that is provided by `PushbackInputStream` for byte streams. See Character Streams versus Byte Streams for a list of the character-stream classes and their corresponding byte-stream classes.

### Step 2: Remove Calls to Deprecated Classes and Methods

> Of the two classes and five methods that were deprecated in `java.io`, only one seems to give programmers trouble: `DataInputStream`'s `readLine` method. This section talks about the alternatives to this method and provides examples. For alternatives to the other classes and methods that were deprecated in `java.io`, consult Alternatives to Deprecated Classes and Methods in `java.io`.

> Many programs that use the `DataInputStream` `readLine` method can be converted to use the `BufferedReader`'s `readLine` method instead. Sometimes, this change is straightforward. A programmer can simply modify the program so that it creates and uses a `BufferedReader` instead of a `DataInputStream`. Code like this:

> >     DataInputStream d = new DataInputStream(in);

> changes to:

> >     BufferedReader d = new BufferedReader(
> >                            new InputStreamReader(in));

> Let's look at an example from Reading Directly from a URL. That section features the `URLReader` program that calls `DataInputStream` `readLine`. Here's the program in its original 1.0 form with the call to the deprecated API shown in bold:

> >     import java.net.*;
> >     import java.io.*;

> >     class URLReader {
> >         public static void main(String[] args) throws Exception {
> >             URL yahoo = new URL("http://www.yahoo.com/");
> >             DataInputStream in = new DataInputStream(
> >                                      yahoo.openStream());

> >             String inputLine;

> >             while ((inputLine = in.readLine()) != null)
> >                 System.out.println(inputLine);

> >             in.close();
> >         }
> >     }

> To change this program so that it no longer uses deprecated API, we can modify the program to use a `BufferedReader` instead of a `DataInputStream`. Here's the new 1.1 version of this program with the changes shown in bold:

> >     import java.net.*;
> >     import java.io.*;

> >     class URLReader {
> >         public static void main(String[] args) throws Exception {
> >             URL yahoo = new URL("http://www.yahoo.com/");
> >     	BufferedReader in = new BufferedReader(
> >                                     new InputStreamReader(
> >                                     yahoo.openStream()));

> >             String inputLine;

> >             while ((inputLine = in.readLine()) != null)
> >                 System.out.println(inputLine);

> >             in.close();
> >         }
> >     }

> We can make this simple change to `URLReader` because `readLine` is the only `DataInputStream` method used by this program.

> However, `DataInputStream` and `BufferedReader` are not source code compatible (they do not support all of the same methods). If your program uses other `DataInputStream` methods that are not supported by `BufferedReader`, then your conversion may not be this simple.

> For instance, one example in this book, `DataIOTest` from the Using `DataInputStream` and `DataOutputStream`, uses not only `readLine`, but also three of `DataInputStream`'s other `readXXX` methods: `readDouble`, `readInt`, and `readChar`. Here's the 1.0 version of `DataIOTest` with these method calls shown in bold:

> >     import java.io.*;

> >     class DataIOTest {
> >         public static void main(String[] args) throws IOException {

> >             // write the data out
> >             DataOutputStream out = new DataOutputStream(
> >     	    new FileOutputStream("invoice1.txt"));

> >             double[] prices = { 19.99, 9.99, 15.99, 3.99, 4.99 };
> >             int[] units = { 12, 8, 13, 29, 50 };
> >             String[] descs = { "Java T-shirt",
> >                                "Java Mug",
> >                                "Duke Juggling Dolls",
> >                                "Java Pin",
> >                                "Java Key Chain" };

> >             for (int i = 0; i < prices.length; i ++) {
> >                 out.writeDouble(prices[i]);
> >                 out.writeChar('\t');
> >                 out.writeInt(units[i]);
> >                 out.writeChar('\t');
> >                 out.writeChars(descs[i]);
> >                 out.writeChar('\n');
> >             }
> >             out.close();

> >             // read it in again
> >             DataInputStream in = new DataInputStream(
> >                 new FileInputStream("invoice1.txt"));

> >             double price;
> >             int unit;
> >             String desc;
> >             double total = 0.0;

> >             try {
> >                 while (true) {
> >                     price = in.readDouble();
> >                     in.readChar();       // throws out the tab
> >                     unit = in.readInt();
> >                     in.readChar();       // throws out the tab
> >                     desc = in.readLine();
> >                     System.out.println("You've ordered " +
> >                                         unit + " units of " +
> >                                         desc + " at $" + price);
> >                     total = total + unit * price;
> >                 }
> >             } catch (EOFException e) {
> >             }
> >             System.out.println("For a TOTAL of: $" + total);
> >             in.close();
> >         }
> >     }

> The solution of swapping a `BufferedReader` for the `DataInputStream` won't work in this program because `BufferedReader` does not support `readDouble`, `readInt`, or `readChar`.

> In this situation, you have three choices:

> 1. Change the algorithm so that the program doesn't need to read lines.
> 2. Use an `ObjectInputStream` in place of the `DataInputStream`. (`ObjectInputStream` provides methods for reading all of Java's primitive data types and so supports `readDouble`, `readInt`, or `readChar` as well as `readLine`.)
> 3. Call `DataInputStream`'s `readChar` method iteratively.
> The first option won't work for most programs. Because `DataIOTest` is an example of how to use `DataInputStream` it doesn't really make sense to use an `ObjectInputStream` instead. So for this example, option #2 won't work. But, this situation is a bit unusual and it's likely that `ObjectInputStream` will serve most programmers better (who wants to re-write `readLine` all the time especially given that a newline character is differs by platform?). So, we'll show you two solutions for the `DataIOTest` example one implementing option #2 and one implementing option #3.

> Here's `DataIOTest` modified to use an `ObjectInputStream` instead of a `DataInputStream`. Note that the `DataOutputStream` had to be changed to an `ObjectOutputStream` so that the data written by it could be read by the `ObjectInputStream`.

> >     import java.io.*;

> >     class DataIOTest {
> >         public static void main(String[] args) throws IOException {

> >             // write the data out
> >             ObjectOutputStream out = new ObjectOutputStream(new
> >                                        FileOutputStream("invoice1.txt"));

> >     	// ...
> >     	// unchanged code removed for the sake of brevity
> >     	// ...

> >             // read it in again
> >             ObjectInputStream in = new ObjectInputStream(new
> >                                        FileInputStream("invoice1.txt"));
> >     	// ...
> >     	// unchanged code removed for the sake of brevity
> >     	// ...

> >         }
> >     }

> And here's another version modified to use `DataInputStream`'s `readChar` method iteratively instead of `readLine`:

> >     import java.io.*;

> >     class DataIOTest {
> >         public static void main(String[] args) throws IOException {

> >     	// ...
> >     	// unchanged code removed for the sake of brevity
> >     	// ...

> >             double price;
> >             int unit;
> >             StringBuffer desc;
> >             double total = 0.0;

> >             try {
> >                 while (true) {
> >                     price = in.readDouble();
> >                     in.readChar();       // throws out the tab
> >                     unit = in.readInt();
> >                     in.readChar();       // throws out the tab
> >                     char chr;
> >                     desc = new StringBuffer(20);
> >                     char lineSep = System.getProperty(
> >                                           "line.separator").charAt(0);
> >                     while ((chr = in.readChar()) != lineSep)
> >                         desc.append(chr);
> >                     System.out.println("You've ordered " +
> >                                         unit + " units of " +
> >                                         desc + " at $" + price);
> >                     total = total + unit * price;
> >                 }
> >             } catch (EOFException e) {
> >             }
> >             System.out.println("For a TOTAL of: $" + total);
> >             in.close();
> >         }
> >     }

| Start of Tutorial > Start of Trail > Start of Lesson | Search