| ## Java 1.1 Unleashed # - 12 - The I/O Package by Michael Morrison IN THIS CHAPTER Input Stream and Reader Classes Output Stream and Writer Classes File Classes It would be impossible for a program to do anything useful without performing some kind of input or output of data. Most programs require input from the user; in return, they output information to the screen, printer, and often to files. The Java I/O package provides an extensive set of classes that handle input and output to and from many different devices. In this chapter, you learn about the primary classes contained in the I/O package, along with some examples that show off the capabilities of these classes. The I/O package, also known as `java.io`, contains many classes, each with a variety of member variables and methods. This chapter does not take an exhaustive look at every class and method contained in the I/O package. Instead, you can view this chapter as a tutorial on how to perform basic input and output using the more popular I/O classes. Armed with the information from this chapter, you will be ready to begin using the Java I/O classes in your own programs. And should you choose to explore the more complex I/O classes supported by Java, you will be prepared for the challenge. ## Input Stream and Reader Classes The Java input model is based on the concept of an input stream. An input stream can be thought of much like a physical (and certainly more literal) stream of water flowing from a water plant into the pipes of a water system. The obvious difference is that an input stream deals with binary computer data rather than physical water. The comparison is relevant, however, because the data going into an input stream flows like the water being pumped into a pipe. Data pumped into an input stream can be directed in many different ways, much as water is directed through the complex system of pipes that make up a water system. The data in an input stream is transmitted one byte at a time, which is roughly analogous to individual drops of water flowing into a pipe. More practically speaking, Java uses input streams as the means of reading data from an input source, such as the keyboard. The basic input stream classes supported by Java follow: `InputStream` `BufferedInputStream` `DataInputStream` `FileInputStream` `StringBufferInputStream` Java version 1.1 introduces support for character input streams, which are virtually identical to the input streams just listed except that they operate on characters rather than on bytes. The character input stream classes are called readers instead of input streams. Corresponding reader classes implement methods similar to the input stream classes just listed except for the `DataInputStream` class. The purpose of providing character-based versions of the input stream classes is to help facilitate the internationalization of character information. To find out more about the support for internationalization in Java, check out Chapter 52, "Java Internationalization." Following are the basic reader classes provided by Java: `Reader` `BufferedReader` `FileReader` `StringReader` Throughout this chapter, you use both input streams and reader classes. Because their supported methods are very similar, it's pretty easy to use one once you've learned about the other. ### The InputStream Class The `InputStream` class is an abstract class that serves as the base class for all other input stream classes. `InputStream` defines a basic interface for reading streamed bytes of information. The methods defined by the `InputStream` class will become very familiar to you because they serve a similar purpose in every `InputStream`-derived class. This design approach enables you to learn the protocol for managing input streams once and then apply it to different devices using an `InputStream`-derived class. The typical scenario when using an input stream is to create an `InputStream`-derived object and then tell it you want to input information by calling an appropriate method. If no input information is currently available, the `InputStream` uses a technique known as blocking to wait until input data becomes available. An example of when blocking takes place is the case of using an input stream to read information from the keyboard. Until the user types information and presses Return or Enter, there is no input available to the `InputStream` object. The `InputStream` object then waits (blocks) until the user presses Return or Enter, at which time the input data becomes available and the `InputStream` object can process it as input. The `InputStream` class defines the following methods: `abstract int read()` `int read(byte` b`[])` `int read(byte` b`[], int` off`, int` len`)` `long skip(long` n`)` `int available()` `synchronized void mark(int` readlimit`)` `synchronized void reset()` `boolean markSupported()` `void close()` `InputStream` defines three different `read()` methods for reading input data in various ways. The first `read()` method takes no parameters and simply reads a byte of data from the input stream and returns it as an integer. This version of `read()` returns `-1` if the end of the input stream is reached. Because this version of `read()` returns a byte of input as an `int`, you must cast it to a `char` if you are reading characters. The second version of `read()` takes an array of bytes as its only parameter, enabling you to read multiple bytes of data at once. The data that is read is stored in this array. You have to make sure that the byte array passed into `read()` is large enough to hold the information being read or an `IOException` is thrown. This version of `read()` returns the actual number of bytes read, or `-1` if the end of the stream is reached. The last version of `read()` takes a byte array, an integer offset, and an integer length as parameters. This version of `read()` is very similar to the second version except that it enables you to specify where in the byte array you want to place the information that is read. The off parameter specifies the offset into the byte array to start placing read data, and the len parameter specifies the maximum number of bytes to read. The `skip()` method is used to skip over bytes of data in the input stream. `skip()` takes a `long` value n as its only parameter, which specifies how many bytes of input to skip. It returns the actual number of bytes skipped or `-1` if the end of the input stream is reached. The `available()` method is used to determine the number of bytes of input data that can be read without blocking. `available()` takes no parameters and returns the number of available bytes. This method is useful if you want to ensure that there is input data available (and therefore avoid the blocking mechanism). The `mark()` method marks the current position in the stream. You can later return to this position using the `reset()` method. The `mark()` and `reset()` methods are useful in situations in which you want to read ahead in the stream but not lose your original position. An example of this situation is verifying a file type, such as an image file. You would probably read the file header first and mark the position at the end of the header. You would then read some of the data to make sure that it follows the format expected for that file type. If the data doesn't look right, you can reset the read pointer and try a different technique. Notice that the `mark()` method takes an integer parameter, readlimit. readlimit specifies how many bytes can be read before the mark becomes invalidated. In effect, readlimit determines how far you can read ahead and still be able to reset the marked position. The `markSupported()` method returns a boolean value representing whether or not an input stream supports the mark/reset functionality. Finally, the `close()` method closes an input stream and releases any resources associated with the stream. It is not necessary to explicitly call `close()` because input streams are automatically closed when the `InputStream` object is destroyed. Although it is not necessary, calling `close()` immediately after you are finished using a stream is a good programming practice. The reason is that `close()` causes the stream buffer to be flushed, which helps avoid file corruption. ### The System.in Object The keyboard is the standard device for retrieving user input. The `System` class contained in the language package contains a member variable that represents the keyboard, or standard input stream. This member variable is called `in` and is an instance of the `InputStream` class. This variable is useful for reading user input from the keyboard. Listing 12.1 contains the `ReadKeys1` program, which shows how the `System.in` object can be connected with an `InputStreamReader` object to read user input from the keyboard. This program can be found in the file `ReadKeys1.java` on the CD-ROM that accompanies this book. NOTE: I mentioned the keyboard as being the standard input stream. This isn't entirely true because the standard input stream can receive input from any number of sources. Although the keyboard certainly is the most common method of feeding input to the standard input stream, it is not the only method. An example of the standard input stream being driven by a different input source is the redirection of an input file into a stream. #### Listing 12.1. The ReadKeys1 class.
    import java.io.*;
    class ReadKeys1 {
      public static void main (String args[]) {
        StringBuffer s = new StringBuffer();
        char c;
        try {
          Reader in = new InputStreamReader(System.in);
          while ((c = (char)in.read()) != `\n') {
            s.append(c);
          }
        }
        catch (Exception e) {
          System.out.println("Error: " + e.toString());
        }
        System.out.println(s);
      }
} The `ReadKeys1` class first creates a `StringBuffer` object called `s`. It then creates a reader object connected to the standard input stream and enters a `while` loop that repeatedly calls the `read()` method until a newline character (`\n`) is detected (that is, the user presses Return). Notice that the input data returned by `read()` is cast to a `char` type before being stored in the character variable `c`. Each time a character is read, it is appended to the string buffer using the `append()` method of `StringBuffer`. It is important to see how any errors caused by the `read()` method are handled by the `try`/`catch` exception-handling blocks. The `catch` block simply prints an error message to the standard output stream based on the error that occurred. Finally, when a newline character is read from the input stream, the `println()` method of the standard output stream is called to output the string to the screen. You learn more about the standard output stream a little later in this chapter. Listing 12.2 contains `ReadKeys2`, which is similar to `ReadKeys1` except that it uses the second version of the `read()` method. This `read()` method takes an array of characters as a parameter to store the input that is read. `ReadKeys2` can be found in the file `ReadKeys2.java` on the CD-ROM that accompanies this book. #### Listing 12.2. The ReadKeys2 class.
    import java.io.*;
    class ReadKeys2 {
      public static void main (String args[]) {
        char buf[] = new char[80];
        try {
          Reader in = new InputStreamReader(System.in);
          in.read(buf, 0, 80);
        }
        catch (Exception e) {
          System.out.println("Error: " + e.toString());
        }
        String s = new String(buf);
        System.out.println(s);
      }
} In `ReadKeys2`, an array of characters 80 characters long is created. A reader object is created and connected to the standard input stream, and a single `read()` method call is performed that reads everything the user has typed. The input is blocked until the user presses Return, at which time the input becomes available and the `read()` method fills the character array with the new data. A `String` object is then created to hold the constant string previously read. Notice that the constructor used to create the `String` object takes an array of characters (`buf`) as the first parameter. Finally, `println()` is again used to output the string. The `ReadKeys3` program in Listing 12.3 shows how to use the last version of the `read()` method. This version of `read()` again takes an array of characters, as well as an offset and length for determining how to store the input data in the character array. `ReadKeys3` can be found in the file `ReadKeys3.java` on the CD-ROM that accompanies this book. #### Listing 12.3. The ReadKeys3 class.
    import java.io.*;
    class ReadKeys3 {
      public static void main (String args[]) {
        char buf[] = new char[10];
        try {
          Reader in = new InputStreamReader(System.in);
          in.read(buf, 0, 10);
        }
        catch (Exception e) {
          System.out.println("Error: " + e.toString());
        }
        String s = new String(buf);
        System.out.println(s);
      }
} `ReadKeys3` is very similar to `ReadKeys2`, with one major difference: The third version of the `read()` method is used to limit the maximum number of characters read into the array. The size of the character array is also shortened to 10 characters to show what this version of `read()` does when more data is available than the array can hold. Remember that this version of `read()` can also be used to read data into a specific offset of the array. In this case, the offset is specified as `0` so that the only difference is the maximum number of characters that can be read (`10`). This useful technique guarantees that you don't overrun the array. ### The BufferedInputStream Class As its name implies, the `BufferedInputStream` class provides a buffered stream of input. This means that more data is read into the buffered stream than you might have requested so that subsequent reads come straight out of the buffer rather than from the input device. This arrangement can result in much faster read access because reading from a buffer is really just reading from memory. `BufferedInputStream` implements all the same methods defined by `InputStream`. As a matter of fact, it doesn't implement any new methods of its own. However, the `BufferedInputStream` class does have two different constructors, which follow: `BufferedInputStream(InputStream` in`)` `BufferedInputStream(InputStream` in`, int` size`)` Notice that both constructors take an `InputStream` object as the first parameter. The only difference between the two is the size of the internal buffer. In the first constructor, a default buffer size is used; in the second constructor, you specify the buffer size with the size integer parameter. To support buffered input, the `BufferedInputStream` class also defines a handful of member variables, which follow: `byte` buf`[]` `int` count `int` pos `int` markpos `int` marklimit The buf byte array member is the buffer in which input data is actually stored. The count member variable keeps up with how many bytes are stored in the buffer. The pos member variable keeps up with the current read position in the buffer. The markpos member variable specifies the current mark position in the buffer as set using the `mark()` method. markpos is equal to `-1` if no mark has been set. Finally, the marklimit member variable specifies the maximum number of bytes that can be read before the mark position is no longer valid. marklimit is set by the readlimit parameter passed into the `mark()` method. Because all these member variables are specified as `protected`, you will probably never actually use any of them. However, seeing these variables should give you some insight into how the `BufferedInputStream` class implements the methods defined by `InputStream`. The `BufferedReader` class is very similar to the `BufferedInputStream` class except that it deals with characters instead of bytes. Listing 12.4 contains the `ReadKeys4` program, which uses a `BufferedReader` object instead of an `InputStreamReader` object to read input from the keyboard. `ReadKeys4` can be found in the file `ReadKeys4.java` on the CD-ROM that accompanies this book. #### Listing 12.4. The ReadKeys4 class.
    import java.io.*;
    class ReadKeys4 {
      public static void main (String args[]) {
        Reader in = new BufferedReader(new InputStreamReader(System.in));
        char buf[] = new char[10];
        try {
          in.read(buf, 0, 10);
        }
        catch (Exception e) {
          System.out.println("Error: " + e.toString());
        }
        String s = new String(buf);
        System.out.println(s);
      }
} The `BufferedReader` object is created by passing the `System.in` input stream into an `InputStreamReader` object. This approach is necessary because the `BufferedReader()` constructor requires a `Reader`-derived object. From there on, the program is essentially the same as `ReadKeys3`, except that the `read()` method is called on the `BufferedReader` object rather than on an `InputStreamReader` object. ### The DataInputStream Class The `DataInputStream` class is useful for reading primitive Java data types from an input stream in a portable fashion. There is only one constructor for `DataInputStream`, which simply takes an `InputStream` object as its only parameter. This constructor is defined as follows:
    DataInputStream(InputStream in)
`DataInputStream` implements the following useful methods beyond those defined by `InputStream`: `final int skipBytes(int` n`)` `final void readFully(byte` b`[])` `final void readFully(byte` b`[], int` off`, int` len`)` `final boolean readBoolean()` `final byte readByte()` `final int readUnsignedByte()` `final short readShort()` `final int readUnsignedShort()` `final char readChar()` `final int readInt()` `final long readLong()` `final float readFloat()` `final double readDouble()` The `skipBytes()` method works in a manner very similar to `skip()`; the exception is that `skipBytes()` blocks until all bytes are skipped. The number of bytes to skip is determined by the integer parameter n. There are two `readFully()` methods implemented by `DataInputStream`. These methods are similar to the `read()` methods except that they block until all data has been read. The normal `read()` methods block only until some data is available, not all. The `readFully()` methods are to the `read()` methods what `skipBytes()` is to `skip()`. The rest of the methods implemented by `DataInputStream` are variations of the `read()` method for different fundamental data types. The type read by each method is easily identifiable by the name of the method. ### The FileInputStream Class The `FileInputStream` class is useful for performing simple file input. For more advanced file input operations, you will more than likely want to use the `RandomAccessFile` class, discussed a little later in this chapter. The `FileInputStream` class can be instantiated using one of the following three constructors: `FileInputStream(String name)` `FileInputStream(File file)` `FileInputStream(FileDescriptor fdObj)` The first constructor takes a `String` object parameter called name, which specifies the name of the file to use for input. The second constructor takes a `File` object parameter that specifies the file to use for input. You learn more about the `File` object near the end of this chapter. The third constructor for `FileInputStream` takes a `FileDescriptor` object as its only parameter. The `FileInputStream` class functions exactly like the `InputStream` class except that it is geared toward working with files. Similarly, the `FileReader` class functions like the `FileInputStream` class except that it operates on characters instead of bytes. List- ing 12.5 contains the `ReadFile` program, which uses the `FileReader` class to read data from a text file. `ReadFile` can be found in the file `ReadFile.java` on the CD-ROM that accompanies this book. #### Listing 12.5. The ReadFile class.
    import java.io.*;
    class ReadFile {
      public static void main (String args[]) {
        char buf[] = new char[64];
        try {
          Reader in = new FileReader("Grocery.txt");
          in.read(buf, 0, 64);
        }
        catch (Exception e) {
          System.out.println("Error: " + e.toString());
        }
        String s = new String(buf);
        System.out.println(s);
      }
} In `ReadFile`, a `FileReader` object is first created by passing a string with the name of the file (`"Grocery.txt"`) as the input file. The `read()` method is then called to read from the input file into a character array. The character array is then used to create a `String` object, which is in turn used for output. Pretty simple! ### The StringBufferInputStream Class Aside from having a very long name, the `StringBufferInputStream` class is a pretty neat class. `StringBufferInputStream` enables you to use a string as a buffered source of input. `StringBufferInputStream` implements all the same methods defined by `InputStream`, and no more. The `StringBufferInputStream` class has a single constructor, which follows:
    StringBufferInputStream(String s)
The constructor takes a `String` object, from which it constructs the string buffer input stream. Although `StringBufferInputStream` doesn't define any additional methods, it does provide a few of its own member variables, which follow: `String` buffer `int` count `int` pos The buffer string member is the buffer in which the string data is actually stored. The count member variable specifies the number of characters to use in the buffer. Finally, the pos member variable keeps up with the current position in the buffer. As is true with the `BufferedInputStream` class, you will probably never see these member variables, but they are important in understanding how the `StringBufferInputStream` class is implemented. The `StringReader` class is a character version of the `StringBufferInputStream` class. Listing 12.6 shows the `ReadString` program, which uses a `StringReader` object to read data from a string of text data. `ReadString` can be found in the file `ReadString.java` on the CD-ROM that accompanies this book. #### Listing 12.6. The ReadString class.
    import java.io.*;
    class ReadString {
      public static void main (String args[]) {
        // Get a string of input from the user
        char buf1[] = new char[64];
        try {
          Reader in = new InputStreamReader(System.in);
          in.read(buf1, 0, 64);
        }
        catch (Exception e) {
          System.out.println("Error: " + e.toString());
        }
        String s1 = new String(buf1);
        // Read the string and output it
        Reader in = new StringReader(s1);
        char buf2[] = new char[64];
        try {
          in.read(buf2, 0, 64);
        }
        catch (Exception e) {
          System.out.println("Error: " + e.toString());
        }
        String s2 = new String(buf2);
        System.out.println(s2);
      }
} The `ReadString` program enables the user to type text, which is read and stored in a string. This string is then used to create a `StringReader` object that is read into another string for output. Obviously, this program goes to a lot of trouble to do very little; it's meant only as a demonstration of how to use the `StringReader` class. It's up to you to find an interesting application to which you can apply this class. The first half of the `ReadString` program should look pretty familiar; it's essentially the guts of the `ReadKeys3` program (discussed earlier in this chapter), which reads data entered by the keyboard into a string. The second half of the program is where you actually get busy with the `StringReader` object. A `StringReader` object is created using the `String` object (`s1`) containing the text entered from the keyboard. The contents of the `StringReader` object are then read into a character array using the `read()` method. The character array is in turn used to construct another `String` object (`s2`), which is output to the screen. ## Output Stream and Writer Classes In Java, output streams are the logical counterparts to input streams; they handle writing data to output sources. Using the water analogy from the discussion of input streams earlier in this chapter, an output stream is equivalent to the water spout on your bathtub. Just as water travels from a water plant through the pipes and out the spout into your bathtub, so must data flow from an input device through the operating system and out an output device. A leaky water spout is an even better way to visualize the transfer of data out of an output stream; each drop of water falling out of the spout represents a byte of data. Each byte of data flows to the output device just like the drops of water falling one after the other out of the bathtub spout. Getting back to Java, you use output streams to output data to various output devices, such as the screen. The primary output stream classes used in Java programming follow: `OutputStream` `PrintStream` `BufferedOutputStream` `DataOutputStream` `FileOutputStream` The Java output streams provide a variety of ways to output data. The `OutputStream` class defines the core behavior required of an output stream. The `PrintStream` class is geared toward outputting text data, such as the data sent to the standard output stream. The `BufferedOutputStream` class is an extension to the `OutputStream` class that provides support for buffered output. The `DataOutputStream` class is useful for outputting primitive data types such as `int` or `float`. Finally, the `FileOutputStream` class provides the support necessary to output data to files. Java also supports character output streams, which are virtually identical to the output streams just listed except that they operate on characters rather than bytes. The character output stream classes are called writers instead of output streams. A corresponding writer class implements methods similar to the output stream classes just listed except for the `DataOutputStream` class. The purpose of providing character-based versions of the output stream classes is to help facilitate the internationalization of character information. To find out more about the support for internationalization in Java, check out Chapter 52, "Java Internationalization." Following are the basic writer classes provided by Java: `Writer` `PrintWriter` `BufferedWriter` `FileWriter` Throughout this chapter, you use both output streams and writer classes. Because their supported methods are very similar, it's pretty easy to use both types after you learn about one of them. ### The OutputStream Class The `OutputStream` class is the output counterpart to `InputStream`; it serves as an abstract base class for all the other output stream classes. `OutputStream` defines the basic protocol for writing streamed data to an output device. As you did with the methods for `InputStream`, you will become accustomed to the methods defined by `OutputStream` because they act very much the same in every `OutputStream`-derived class. The benefit of this common interface is that you can essentially learn a method once and then be able to apply it to different classes without starting the learning process over again. You typically create an `OutputStream`-derived object and call an appropriate method to tell it you want to output information. The `OutputStream` class uses a technique similar to the one used by `InputStream`: It blocks until data has been written to an output device. While blocking (waiting for the current output to be processed), the `OutputStream` class does not allow any further data to be output. The `OutputStream` class implements the following methods: `abstract void write(int` b`)` `void write(byte` b`[])` `void write(byte` b`[], int` off`, int` len`)` `void flush()` `void close()` `OutputStream` defines three different `write()` methods for writing data in a few different ways. The first `write()` method writes a single byte to the output stream, as specified by the integer parameter b. The second version of `write()` takes an array of bytes as a parameter and writes them to the output stream. The last version of `write()` takes a byte array, an integer offset, and a length as parameters. This version of `write()` is very much like the second version except that it uses the other parameters to determine where in the byte array to begin outputting data, along with how much data to output. The off parameter specifies an offset into the byte array from which you want to begin outputting data, and the len parameter specifies how many bytes are to be output. The `flush()` method is used to flush the output stream. Calling `flush()` forces the `OutputStream` object to output any pending data. Finally, the `close()` method closes an output stream and releases any resources associated with the stream. As with `InputStream` objects, it isn't usually necessary to call `close()` on an `OutputStream` object because streams are automatically closed when they are destroyed. ### The PrintStream Class The `PrintStream` class is derived from `OutputStream` and is designed primarily for printing output data as text. `PrintStream` has two constructors: `PrintStream(OutputStream` out`)` `PrintStream(OutputStream` out`, boolean` autoflush`)` Both `PrintStream` constructors take an `OutputStream` object as their first parameter. The only difference between the two methods is how the newline character is handled. In the first constructor, the stream is flushed based on an internal decision by the object. In the second constructor, you can specify that the stream be flushed every time it encounters a newline character. You specify this through the boolean autoflush parameter. The `PrintStream` class also implements a rich set of methods, which follow: `boolean checkError()` `void print(Object` obj`)` `synchronized void print(String` s`)` `synchronized void print(char` s`[])` `void print(char` c`)` `void print(int` i`)` `void print(long` l`)` `void print(float` f`)` `void print(double` d`)` `void print(boolean` b`)` `void println()` `synchronized void println(Object` obj`)` `synchronized void println(String` s`)` `synchronized void println(char` s`[])` `synchronized void println(char` c`)` `synchronized void println(int` I`)` `synchronized void println(long` l`)` `synchronized void println(float` f`)` `synchronized void println(double` d`)` `synchronized void println(boolean` b`)` The `checkError()` method flushes the stream and returns whether or not an error has occurred. The return value of `checkError()` is based on whether an error has ever occurred on the stream, meaning that once an error occurs, `checkError()` always returns `true` for that stream. `PrintStream` provides a variety of `print()` methods to handle all your printing needs. The version of `print()` that takes an `Object` parameter simply outputs the results of calling the `toString()` method on the object. Each of the other `print()` methods takes a different type parameter that specifies which data type is to be printed. The `println()` methods implemented by `PrintStream` are very similar to the `print()` methods. The only difference is that the `println()` methods print a newline character following the data that is printed. The `println()` method that takes no parameters simply prints a newline character by itself. ### The System.out Object The monitor is the primary output device on modern computer systems. The `System` class has a member variable that represents the standard output stream, which is typically the monitor. The member variable is called `out` and is an instance of the `PrintStream` class. The `out` member variable is very useful for outputting text to the screen. But you already know this because you've seen the `out` member variable in most of the sample programs developed thus far. ### The BufferedOutputStream Class The `BufferedOutputStream` class is very similar to the `OutputStream` class except that it provides a buffered stream of output. This class enables you to write to a stream without causing a bunch of writes to an output device. The `BufferedOutputStream` class maintains a buffer that is written to when you write to the stream. When the buffer gets full or is explicitly flushed, it is written to the output device. This output approach is much more efficient because most of the data transfer takes place in memory. And when it does come time to output the data to a device, it all happens at once. The `BufferedOutputStream` class implements the same methods defined in `OutputStream`, meaning that there are no additional methods except for constructors. The two constructors for `BufferedOutputStream` follow: `BufferedOutputStream(OutputStream` out`)` `BufferedOutputStream(OutputStream` out`, int` size`)` Both constructors for `BufferedOutputStream` take an `OutputStream` object as their first parameter. The only difference between the two is the size of the internal buffer used to store the output data. In the first constructor, a default buffer size of 512 bytes is used; in the second constructor, you specify the buffer size with the size integer parameter. The buffer itself within the `BufferedOutputStream` class is managed by two member variables, which follow: `byte` buf`[]` `int` count The buf byte array member variable is the actual data buffer in which output data is stored. The count member keeps up with how many bytes are in the buffer. These two member variables are sufficient to represent the state of the output stream buffer. The `BufferedWriter` class is a character-based version of the `BufferedOutputStream` class. Listing 12.7 shows the `WriteStuff` program, which uses a `BufferedWriter` object to output a character array of text data. `WriteStuff` can be found in the file `WriteStuff.java` on the CD-ROM that accompanies this book. #### Listing 12.7. The WriteStuff class.
    import java.io.*;
    class WriteStuff {
      public static void main (String args[]) {
        // Copy the string into a byte array
        String s = new String("Dance, spider!\n");
        char[] buf = new char[64];
        s.getChars(0, s.length(), buf, 0);
        // Output the byte array (buffered)
        Writer out = new BufferedWriter(new OutputStreamWriter(System.out));
        try {
          out.write(buf, 0, 64);
          out.flush();
        }
        catch (Exception e) {
          System.out.println("Error: " + e.toString());
        }
      }
} The `WriteStuff` program fills a character array with text data from a string and outputs the array to the screen using a buffered output stream. `WriteStuff` begins by creating a `String` object containing text, and a character array. The `getChars()` method of `String` is used to copy the characters of data in the string to the character array. The `getChars()` method copies each character in the string to the array. Once the character array is ready, a `BufferedWriter` object is created by passing `System.out` into the constructor for an `OutputStreamWriter` object, which in turn is passed to the constructor for the `BufferedWriter` object. The character array is then written to the output buffer using the `write()` method. Because the stream is buffered, it is necessary to call the `flush()` method to actually output the data. ### The DataOutputStream Class The `DataOutputStream` class is useful for writing primitive Java data types to an output stream in a portable way. `DataOutputStream` has only one constructor, which simply takes an `OutputStream` object as its only parameter. This constructor is defined as follows:
    DataOutputStream(OutputStream out)
The `DataOutputStream` class implements the following useful methods beyond those inherited from `OutputStream`: `final int size()` `final void writeBoolean(boolean` v`)` `final void writeByte(int` v`)` `final void writeShort(int` v`)` `final void writeChar(int` v`)` `final void writeInt(int` v`)` `final void writeFloat(float` v`)` `final void writeDouble(double` v`)` `final void writeBytes(String` s`)` `final void writeChars(String` s`)` The `size()` method is used to determine how many bytes have been written to the stream thus far. The integer value returned by `size()` specifies the number of bytes written. The rest of the methods implemented in `DataOutputStream` are all variations of the `write()` method. Each version of `write`Type`()` takes a different data type that is in turn written as output. ### The FileOutputStream Class The `FileOutputStream` class provides a means to perform simple file output. For more advanced file output, check out the `RandomAccessFile` class, discussed a little later in this chapter. A `FileOutputStream` object can be created using one of the following constructors: `FileOutputStream(String` name`)` `FileOutputStream(File` file`)` `FileOutputStream(FileDescriptor` fdObj`)` The first constructor takes a `String` parameter, which specifies the name of the file to use for output. The second constructor takes a `File` object parameter, which specifies the output file. You learn about the `File` object later in this chapter. The third constructor takes a `FileDescriptor` object as its only parameter. The `FileWriter` class functions exactly like the `FileOutputStream` class except that it is specifically designed to work with characters instead of with bytes. Listing 12.8 contains the `WriteFile` program, which uses the `FileWriter` class to write user input to a text file. `WriteFile` can be found in the file `WriteFile.java` on the CD-ROM that accompanies this book. #### Listing 12.8. The WriteFile class.
    import java.io.*;
    class WriteFile {
      public static void main (String args[]) {
        // Read the user input
        char buf[] = new char[64];
        try {
          Reader in = new InputStreamReader(System.in);
          in.read(buf, 0, 64);
        }
        catch (Exception e) {
          System.out.println("Error: " + e.toString());
        }
        // Output the data to a file
        try {
          Writer out = new FileWriter("Output.txt");
          out.write(buf, 0, 64);
          out.flush();
        }
        catch (Exception e) {
          System.out.println("Error: " + e.toString());
        }
      }
} In `WriteFile`, user input is read from the standard input stream into a character array using the `read()` method of `InputStreamReader`. A `FileWriter` object is then created with the filename `Output.txt`, which is passed in as the only parameter to the constructor. The `write()` method is then used to output the character array to the stream. You can see that working with writers is just as easy as working with readers. ## File Classes If the `FileInputStream` and `FileOutputStream` classes don't quite meet your file-handling expectations, don't despair! Java provides two more classes for working with files that are sure to meet your needs. These two classes are `File` and `RandomAccessFile`. The `File` class models an operating system directory entry, providing you with access to information about a file including file attributes and the full path where the file is located, among other things. The `RandomAccessFile` class, on the other hand, provides a variety of methods for reading and writing data to and from a file. ### The File Class The `File` class can be instantiated using one of three constructors, which follow: `File(String` path`)` `File(String` path`, String` name`)` `File(File` dir`, String` name`)` The first constructor takes a single `String` parameter that specifies the full path name of the file. The second constructor takes two `String` parameters: path and name. The path parameter specifies the directory path where the file is located; the name parameter specifies the name of the file. The third constructor is similar to the second except that it takes another `File` object as the first parameter instead of a string. The `File` object in this case is used to specify the directory path of the file. The most important methods implemented by the `File` class follow: `String getName()` `String getPath()` `String getAbsolutePath()` `boolean exists()` `boolean canWrite()` `boolean canRead()` `boolean isFile()` `boolean isDirectory()` `boolean isAbsolute()` `long lastModified()` `long length()` `boolean mkdir()` `boolean mkdirs()` `boolean renameTo(File` dest`)` `boolean delete()` `String[] list()` `String[] list(FilenameFilter` filter`)` The `getName()` method gets the name of a file and returns it as a string. The `getPath()` method returns the path of a file--which may be relative--as a string. The `getAbsolutePath()` method returns the absolute path of a file. The `getParent()` method returns the parent directory of a file or `null` if a parent directory is not found. The `exists()` method returns a boolean value that specifies whether or not a file actually exists. The `canWrite()` and `canRead()` methods return boolean values that specify whether a file can be written to or read from. The `isFile()` and `isDirectory()` methods return boolean values that specify whether a file is valid and whether the directory information is valid. The `isAbsolute()` method returns a boolean value that specifies whether a filename is absolute. The `lastModified()` method returns a `long` value that specifies the time at which a file was last modified. The `long` value returned is useful only in determining differences between modification times; it has no meaning as an absolute time and is not suitable for output. The `length()` method returns the length of a file in bytes. The `mkdir()` method creates a directory based on the current path information. `mkdir()` returns a boolean value indicating the success of creating the directory. The `mkdirs()` method is similar to `mkdir()`, except that it can be used to create an entire directory structure. The `renameTo()` method renames a file to the name specified by the `File` object passed as the dest parameter. The `delete()` method deletes a file. Both `renameTo()` and `delete()` return a boolean value indicating the success or failure of the operation. Finally, the `list()` methods of the `File` object obtain listings of the directory contents. Both `list()` methods return a list of filenames in a `String` array. The only difference between the two methods is that the second version takes a `FilenameFilter` object that enables you to filter out certain files from the list. Listing 12.9 shows the source code for the `FileInfo` program, which uses a `File` object to determine information about a file in the current directory. The `FileInfo` program is located in the `FileInfo.java` source file on the CD-ROM that accompanies this book. #### Listing 12.9. The FileInfo class.
    import java.io.*;
    class FileInfo {
      public static void main (String args[]) {
        System.out.println("Enter file name: ");
        char c;
        StringBuffer buf = new StringBuffer();
        try {
          Reader in = new InputStreamReader(System.in);
          while ((c = (char)in.read()) != `\n')
            buf.append(c);
        }
        catch (Exception e) {
          System.out.println("Error: " + e.toString());
        }
        File file = new File(buf.toString());
        if (file.exists()) {
          System.out.println("File Name  : " + file.getName());
          System.out.println("     Path  : " + file.getPath());
          System.out.println("Abs. Path  : " + file.getAbsolutePath());
          System.out.println("Writable   : " + file.canWrite());
          System.out.println("Readable   : " + file.canRead());
          System.out.println("Length     : " + (file.length() / 1024) + "KB");
        }
        else
          System.out.println("Sorry, file not found.");
      }
} The `FileInfo` program uses the `File` object to get information about a file in the current directory. The user is first prompted to type a filename; the resulting input is stored in a `String` object. The `String` object is then used as the parameter to the `File` object's constructor. A call to the `exists()` method determines whether the file actually exists. If so, information about the file is obtained through the various `File()` methods and the results are output to the screen. Following are the results of running `FileInfo` and specifying `FileInfo.java` as the file for which you want to get information:
    File Name  : FileInfo.java
         Path  : FileInfo.java
    Abs. Path  : C:\Books\Jul11PRE\Source\Chap12\FileInfo.java
    Writable   : true
    Readable   : true
    Length     : 0KB
### The RandomAccessFile Class The `RandomAccessFile` class provides a multitude of methods for reading and writing to and from files. Although you can certainly use `FileInputStream` and `FileOutputStream` for file I/O, `RandomAccessFile` provides many more features and options. Following are the constructors for `RandomAccessFile`: `RandomAccessFile(String` name`, String` mode`)` `RandomAccessFile(File` file`, String` mode`)` The first constructor takes a `String` parameter specifying the name of the file to access, along with a `String` parameter specifying the type of mode (read or write). The mode type can be either `"r"` for read mode or `"rw"` for read/write mode. The second constructor takes a `File` object as the first parameter, which specifies the file to access. The second parameter is a mode string, which works exactly the same as it does in the first constructor. The `RandomAccessFile` class implements a variety of powerful file I/O methods. Following are some of the most useful ones: `int skipBytes(int` n`)` `long getFilePointer()` `void seek(long` pos`)` `int read()` `int read(byte` b`[])` `int read(byte` b`[], int` off`, int` len`)` `final boolean readBoolean()` `final byte readByte()` `final int readUnsignedByte()` `final short readShort()` `final int readUnsignedShort()` `final char readChar()` `final int readInt()` `final long readLong()` `final float readFloat()` `final double readDouble()` `final String readLine()` `final void readFully(byte` b`[])` `final void readFully(byte` b`[], int` off`, int` len`)` `void write(byte` b`[])` `void write(byte` b`[], int` off`, int` len`)` `final void writeBoolean(boolean` v`)` `final void writeByte(int` v`)` `final void writeShort(int` v`)` `final void writeChar(int` v`)` `final void writeInt(int` v`)` `final void writeLong(long` v`)` `void writeFloat(float` v`)` `void writeDouble(double` v`)` `void writeBytes(String` s`)` `void writeChars(String` s`)` `long length()` `void close()` From looking at this method list, you no doubt are thinking that many of these methods look familiar. And they should look familiar--most of the methods implemented by `RandomAccessFile` are also implemented by either `FileInputStream` or `FileOutputStream`. The fact that `RandomAccessFile` combines them into a single class is a convenience in and of itself. But you already know how to use these methods because they work just as they do in the `FileInputStream` and `FileOutputStream` classes. What you are interested in are the new methods implemented by `RandomAccessFile`. The first new method you may have noticed is the `getFilePointer()` method. `getFilePointer()` returns the current position of the file pointer as a `long` value. The file pointer indicates the location in the file where data will next be read from or written to. In read mode, the file pointer is analogous to the needle on a phonograph or the laser in a CD player. The `seek()` method is the other new method that should catch your attention. `seek()` sets the file pointer to the absolute position specified by the `long` parameter pos. Calling `seek()` to move the file pointer is analogous to moving the phonograph needle with your hand. In both cases, the read point of the data or music is being moved. A similar situation occurs when you are writing data. NOTE: It's worth noting that Java's file pointers are not pointers in the traditional sense (that is, a pointer meaning a direct reference to a memory location). As you may know, Java doesn't support memory pointers. File pointers are more conceptual in nature: They "point" to a specific location in a file. Listing 12.10 shows the source code for `FilePrint`, a program that uses the `RandomAccessFile` class to print a file to the screen. The source code for the `FilePrint` program can be found in the file `FilePrint.java` on the CD-ROM that accompanies this book. #### Listing 12.10. The FilePrint class.
    import java.io.*;
    class FilePrint {
      public static void main (String args[]) {
        System.out.println("Enter file name: ");
        char c;
        StringBuffer buf = new StringBuffer();
        try {
          Reader in = new InputStreamReader(System.in);
          while ((c = (char)in.read()) != `\n')
            buf.append(c);
          System.out.println(buf.toString());
          RandomAccessFile file = new RandomAccessFile(buf.toString(), "rw");
          while (file.getFilePointer() < file.length())
            System.out.println(file.readLine());
        }
        catch (Exception e) {
          System.out.println("Error: " + e.toString());
        }
      }
} The `FilePrint` program begins very much like the `FileInfo` program in Listing 12.9 in that it prompts the user to type a filename and stores the result in a string. It then uses that string to create a `RandomAccessFile` object in read/write mode, which is specified by passing `"rw"` as the second parameter to the constructor. A `while` loop is then used to repeatedly call the `readLine()` method until the entire file has been read. The call to `readLine()` is performed within a call to `println()` so that each line of the file is output to the screen. ## Summary Whew! This chapter covered a lot of ground! Hopefully, you've managed to make it this far relatively unscathed. On the up side, you've learned just about all there is to know about fundamental Java I/O and the most important classes in the I/O package. That's not to say that there isn't still a wealth of information inside the I/O package that you haven't seen. The point is that the Java class libraries are very extensive, which means that some of the classes are useful only in very special circumstances. The goal of this chapter was to highlight the more mainstream classes and methods within the I/O package. One of the neatest uses of I/O streams is in sending and receiving data across a network. You're in luck because the next chapter takes a look at the Java networking package, `java.net`. You learn all about the built-in networking support provided by the networking package and how it can be used to build network Java programs.
| ©Copyright, Macmillan Computer Publishing. All rights reserved.