Migrating to 1.1 |
To support internationalization, the JDK 1.1 release adds character streams to thejava.io
package. In addition, a few of the package's classes and methods have been deprecated. Converting a 1.0 program that usesjava.io
API usually requires two steps:The following two sections provide details and examples to help you with these two steps.
- 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.
- Make sure that the program does not use any deprecated API.
Step 1: If Appropriate, Switch from Byte Streams to Character Streams
Does the program use a byte stream (an object that inherits fromInputStream
orOutputStream
)? 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
andWriter
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, theOutputStreamWriter
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 byPushbackInputStream
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 injava.io
, only one seems to give programmers trouble:DataInputStream
'sreadLine
method. This section talks about the alternatives to this method and provides examples. For alternatives to the other classes and methods that were deprecated injava.io
, consult Alternatives to Deprecated Classes and Methods injava.io
.Many programs that use the
DataInputStream
readLine
method can be converted to use theBufferedReader
'sreadLine
method instead. Sometimes, this change is straightforward. A programmer can simply modify the program so that it creates and uses aBufferedReader
instead of aDataInputStream
. Code like this:changes to:DataInputStream d = new DataInputStream(in);Let's look at an example from Reading Directly from a URL. That section features theBufferedReader d = new BufferedReader(new InputStreamReader(in));URLReader
program that callsDataInputStream
readLine
. Here's the program in its original 1.0 form with the call to the deprecated API shown in bold:To change this program so that it no longer uses deprecated API, we can modify the program to use aimport 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(); } }BufferedReader
instead of aDataInputStream
. Here's the new 1.1 version of this program with the changes shown in bold:We can make this simple change toimport 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(); } }URLReader
becausereadLine
is the onlyDataInputStream
method used by this program.However,
DataInputStream
andBufferedReader
are not source code compatible (they do not support all of the same methods). If your program uses otherDataInputStream
methods that are not supported byBufferedReader
, then your conversion may not be this simple.For instance, one example in this book,
DataIOTest
from the UsingDataInputStream
andDataOutputStream
, uses not onlyreadLine
, but also three ofDataInputStream
's otherreadXXX
methods:readDouble
,readInt
, andreadChar
. Here's the 1.0 version ofDataIOTest
with these method calls shown in bold:
The solution of swapping aimport 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(); } }BufferedReader
for theDataInputStream
won't work in this program becauseBufferedReader
does not supportreadDouble
,readInt
, orreadChar
.In this situation, you have three choices:
The first option won't work for most programs. Because
- Change the algorithm so that the program doesn't need to read lines.
- Use an
ObjectInputStream
in place of theDataInputStream
. (ObjectInputStream
provides methods for reading all of Java's primitive data types and so supportsreadDouble
,readInt
, orreadChar
as well asreadLine
.)- Call
DataInputStream
'sreadChar
method iteratively.DataIOTest
is an example of how to useDataInputStream
it doesn't really make sense to use anObjectInputStream
instead. So for this example, option #2 won't work. But, this situation is a bit unusual and it's likely thatObjectInputStream
will serve most programmers better (who wants to re-writereadLine
all the time especially given that a newline character is differs by platform?). So, we'll show you two solutions for theDataIOTest
example one implementing option #2 and one implementing option #3.Here's
DataIOTest
modified to use anObjectInputStream
instead of aDataInputStream
. Note that theDataOutputStream
had to be changed to anObjectOutputStream
so that the data written by it could be read by theObjectInputStream
.And here's another version modified to useimport 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 // ... } }DataInputStream
'sreadChar
method iteratively instead ofreadLine
: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(); } }
Migrating to 1.1 |