20.6. Priority Queues

 
[Page 608 ( continued )]

18.4. Binary I/O Classes

The design of the Java I/O classes is a good example of applying inheritance, where common operations are generalized in superclasses, and subclasses provide specialized operations. Figure 18.3 lists some of the classes for performing binary I/O. InputStream is the root for binary input classes, and OutputStream is the root for binary output classes. Figures 18.4 and 18.5 list all the methods in InputStream and OutputStream .

Figure 18.3. InputStream , OutputStream , and their subclasses are for binary I/O.

Figure 18.4. The abstract InputStream class defines the methods for the input stream of bytes.


[Page 609]
Figure 18.5. The abstract OutputStream class defines the methods for the output stream of bytes.

Note

All the methods in the binary I/O classes are declared to throw java.io.IOException or a subclass of java.io.IOException .


18.4.1. FileInputStream/FileOutputStream

FileInputStream/FileOutputStream is for reading/writing bytes from/to files. All the methods in these classes are inherited from InputStream and OutputStream . FileInputStream/FileOutputStream does not introduce new methods. To construct a FileInputStream , use the following constructors, as shown in Figure 18.6:

Figure 18.6. FileInputStream inputs stream of bytes from a file.

A java.io.FileNotFoundException will occur if you attempt to create a FileInputStream with a nonexistent file.

To construct a FileOutputStream , use the following constructors, as shown in Figure 18.7.

Figure 18.7. FileOutputStream outputs stream of bytes to a file.

If the file does not exist, a new file will be created. If the file already exists, the first two constructors will delete the current content of the file. To retain the current content and append new data into the file, use the last two constructors by passing true to the append parameter.


[Page 610]

Almost all the methods in the I/O classes throw java.io.IOException . Therefore you have to declare java.io.IOException to throw in the method or place the code in a try-catch block, as shown below:

Listing 18.1 uses binary I/O to write ten byte values from 1 to 10 to a file named temp.dat and reads them back from the file.

Listing 18.1. TestFileStream.java
 1   import   java.io.*;  2  3   public class   TestFileStream {  4   public static void   main(String[] args)    throws   IOException  {  5  // Create an output stream to the file  6  FileOutputStream output =   new   FileOutputStream(   "temp.dat"   );  7  8  // Output values to the file  9   for   (   int   i =   1   ; i <=   10   ; i++) 10  output.write(i);  11 12  // Close the output stream  13  output.close();  14 15  // Create an input stream for the file  16  FileInputStream input =   new   FileInputStream(   "temp.dat"   );  17 18  // Read values from the file  19   int   value; 20   while   ((value =  input.read()  ) !=   -1   ) 21        System.out.print(value +   " "   ); 22 23  // Close the output stream  24  input.close();  25    } 26  } 

A FileOutputStream is created for file temp.dat in line 6. The for loop writes ten byte values into the file (lines 9 “10). Invoking write(i) is the same as invoking write((byte)i) . Line 13 closes the output stream. Line 16 creates a FileInputStream for file temp.dat. Values are read from the file and displayed on the console in lines 19 “21. The expression ((value = input.read()) != -1) (line 20) reads a byte from input.read() , assigns it to value , and checks whether it is -1 . The input value of -1 signifies the end of a file .

The file temp.dat created in this example is a binary file. It can be read from a Java program but not from a text editor, as shown in Figure 18.8.

Figure 18.8. A binary file cannot be displayed in text mode.
(This item is displayed on page 611 in the print version)


Tip

When a stream is no longer needed, always close it using the close() method. Not closing streams may cause data corruption in the output file, or other programming errors.



[Page 611]

Note

Text I/O is built upon binary I/O. PrinterWriter and Scanner are implemented using binary I/O classes. An instance of FileInputStream can be used as an argument to construct a Scanner , and an instance of FileOutputStream can be used as an argument to construct a PrintWriter . You can create a PrintWriter to append text into a file using

   new   PrintWriter(   new   FileOutputStream(   "temp.txt"   ,   true   )); 

If temp.txt does not exist, it is created. If temp.txt already exists, new data is appended to the file.


18.4.2. FilterInputStream/FilterOutputStream

Filter streams are streams that filter bytes for some purpose. The basic byte input stream provides a read method that can only be used for reading bytes. If you want to read integers, doubles, or strings, you need a filter class to wrap the byte input stream. Using a filter class enables you to read integers, doubles, and strings instead of bytes and characters . FilterInputStream and FilterOutputStream are the base classes for filtering data. When you need to process primitive numeric types, use DataInputStream and DataOutputStream to filter bytes.

18.4.3. DataInputStream/DataOutputStream

DataInputStream reads bytes from the stream and converts them into appropriate primitive type values or strings. DataOutputStream converts primitive type values or strings into bytes and outputs the bytes to the stream.

DataInputStream extends FilterInputStream and implements the DataInput interface, as shown in Figure 18.9. DataOutputStream extends FilterOutputStream and implements the DataOutput interface, as shown in Figure 18.10.

Figure 18.9. DataInputStream filters input stream of bytes into primitive data type values and strings.


[Page 612]
Figure 18.10. DataOutputStream enables you to write primitive data type values and strings into an output stream.

DataInputStream implements the methods defined in the DataInput interface to read primitive data type values and strings. DataOutputStream implements the methods defined in the DataOutput interface to write primitive data type values and strings. Primitive values are copied from memory to the output without any conversions. Characters in a string may be written in several ways, as discussed in the next section.

Characters and Strings in Binary I/O

A Unicode consists of two bytes. The writeChar(char c) method writes the Unicode of character c to the output. The writeChars(String s) method writes the Unicode for each character in the string s to the output. The writeBytes(String s) method writes the lower byte of the Unicode for each character in the string s to the output. The high byte of the Unicode is discarded. The writeBytes method is suitable for strings that consist of ASCII characters, since an ASCII code is stored only in the lower byte of a Unicode. If a string consists of non-ASCII characters, you have to use the writeChars method to write the string.

The writeUTF(String s) method writes two bytes of length information to the output stream, followed by the modified UTF-8 representation of every character in the string s. UTF-8 is a coding scheme that allows systems to operate with both ASCII and Unicode. Most operating systems use ASCII. Java uses Unicode. The ASCII character set is a subset of the Unicode character set. Since most applications need only the ASCII character set, it is a waste to represent an 8-bit ASCII character as a 16-bit Unicode character. The modified UTF-8 scheme stores a character using one, two, or three bytes. ASCII values less than 0x7F are coded in one byte. Unicode values less than 0x7FF are coded in two bytes. Other Unicode values are coded in three bytes.

The initial bits of a UTF-8 character indicate whether a character is stored in one byte, two bytes, or three bytes. If the first bit is 0, it is a one-byte character. If the first bits are 110 , it is the first byte of a two-byte sequence. If the first bits are 1110 , it is the first byte of a three-byte sequence. The information that indicates the number of characters in a string is stored in the first two bytes preceding the UTF-8 characters. For example, writeUTF("ABCDEF") actually writes eight bytes to the file because the first two bytes store the number of characters in the string.

The writeUTF(String s) method converts a string into a series of bytes in the UTF-8 format and writes them into a binary stream. The readUTF() method reads a string that has been written using the writeUTF method.


[Page 613]

The UTF-8 format has the advantage of saving a byte for each ASCII character, because a Unicode character takes up two bytes and an ASCII character in UTF-8 takes up only one byte. If most of characters in a long string are regular ASCII characters, using UTF-8 is more efficient.

Using DataInputStream/DataOutputStream

Data streams are used as wrappers on existing input, and output streams to filter data in the original stream. They are created using the following constructors (see Figure 18.9 and Figure 18.10):

   public   DataInputStream(InputStream instream)   public   DataOutputStream(OutputStream outstream) 

The statements given below create data streams. The first statement creates an input stream for file in.dat ; the second statement creates an output stream for file out.dat :

 DataInputStream infile =    new   DataInputStream  (   new   FileInputStream(   "in.dat"   )); DataOutputStream outfile =    new   DataOutputStream  (   new   FileOutputStream(   "out.dat"   )); 

Listing 18.2 writes student names and scores to a file named temp.dat and reads the data back from the file.

Listing 18.2. TestDataStream.java
(This item is displayed on page 614 in the print version)
 1   import   java.io.*;  2  3   public class   TestDataStream {  4   public static void   main(String[] args)   throws   IOException {  5  // Create an output stream for file temp.dat  6  DataOutputStream output =  7    new   DataOutputStream(   new   FileOutputStream(   "temp.dat"   ));  8  9  // Write student test scores to the file  10  output.writeUTF(   "John"   );  11  output.writeDouble(   85.5   );  12      output.writeUTF(   "Jim"   ); 13      output.writeDouble(   185.5   ); 14      output.writeUTF(   "George"   ); 15      output.writeDouble(   105.25   ); 16 17  // Close output stream  18      output.close(); 19 20  // Create an input stream for file temp.dat  21  DataInputStream input =  22    new   DataInputStream(   new   FileInputStream(   "temp.dat"   ));  23 24  // Read student test scores from the file  25      System.out.println(  input.readUTF()  +   " "   +  input.readDouble()  ); 26      System.out.println(input.readUTF() +   " "   + input.readDouble()); 27      System.out.println(input.readUTF() +   " "   + input.readDouble()); 28    } 29  } 

A DataOutputStream is created for file temp.dat in lines 6 “7. Student names and scores are written to the file in lines 10 “15. Line 18 closes the output stream. A DataInputStream is created for the same file in lines 21 “22. Student names and scores are read back from the file and displayed on the console in lines 25 “27.

DataInputStream and DataOutputStream read and write Java primitive type values and strings in a machine-independent fashion, thereby enabling you to write a data file on one machine and read it on another machine that has a different operating system or file structure. An application uses a data output stream to write data that can later be read by a program using a data input stream.


[Page 614]

Caution

You have to read data in the same order and format in which they are stored. For example, since names are written in UTF-8 using writeUTF , you must read names using readUTF .


Tip

If you keep reading data at the end of a DataInputStream , an EOFException will occur. How, then, do you check the end of a file? Use input.available() to check it. input.available() == 0 indicates the end of a file.


18.4.4. BufferedInputStream / BufferedOutputStream

BufferedInputStream / BufferedOutputStream can be used to speed up input and output by reducing the number of reads and writes. BufferedInputStream / BufferedOutputStream does not contain new methods. All the methods in BufferedInputStream / BufferedOutputStream are inherited from the InputStream / OutputStream classes. BufferedInputStream / BufferedOutputStream adds a buffer in the stream for storing bytes for efficient processing.

You may wrap a BufferedInputStream / BufferedOutputStream on any InputStream / OutputStream using the following constructors, as shown in Figure 18.11 and Figure 18.12:

Figure 18.11. BufferedInputStream buffers input stream.

Figure 18.12. BufferedOutputStream buffers output stream.


[Page 615]

If no buffer size is specified, the default size is 512 bytes. A buffered input stream reads as many data as possible into its buffer in a single read call. By contrast, a buffered output stream calls the write method only when its buffer fills up or when the flush() method is called.

You can improve the performance of the TestDataStream program in the preceding example by adding buffers in the stream in lines 6 “7 and 21 “22, as follows :

 DataOutputStream output =   new   DataOutputStream(    new   BufferedOutputStream  (   new   FileOutputStream(   "temp.dat"   ))); DataInputStream input =   new   DataInputStream(    new   BufferedInputStream  (   new   FileInputStream(   "temp.dat"   ))); 

 


Introduction to Java Programming-Comprehensive Version
Introduction to Java Programming-Comprehensive Version (6th Edition)
ISBN: B000ONFLUM
EAN: N/A
Year: 2004
Pages: 503

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net