Byte-Oriented Stream Classes

   

Java™ 2 Primer Plus
By Steven Haines, Steve Potts

Table of Contents
Chapter 12.  Using Java's Input and Output Classes


Byte-oriented stream classes all implement the java.io.InputStream or java.io.OutputStream interface. The java.io.InputStream interface provides the methods shown in Table 12.1. These methods provide information to manage streams: read data from the stream, find out how much data is available in the stream, advance to a new place in the stream, and close the stream.

Table 12.1. java.io.InputStream Methods

Method

Description

int available()

Returns the number of bytes that can be read (or skipped over) from this input stream without blocking by the next caller of a method for this input stream.

void close()

Closes this input stream and releases any system resources associated with the stream.

void mark(int readlimit)

Marks the current position in this input stream.

boolean markSupported()

Tests if this input stream supports the mark and reset methods.

abstract int read()

Reads the next byte of data from the input stream.

int read(byte[] b)

Reads some number of bytes from the input stream and stores them into the buffer array b.

int read(byte[] b, int off, int len)

Reads up to len bytes of data from the input stream into an array of bytes.

void reset()

Repositions this stream to the position at the time the mark method was last called on this input stream.

long skip(long n)

Skips over and discards n bytes of data from this input stream.

All the input stream classes have these methods available to them as a minimum, so it is good for you to see them now.

The java.io.OutputStream class provides the methods shown in Table 12.2; output streams are concerned with writing data to a stream, flushing data that is in the stream out to its destination, and closing the stream.

Table 12.2. java.io.OutputStream Methods

Method

Description

void close()

Closes this output stream and releases any system resources associated with this stream.

void flush()

Flushes this output stream and forces any buffered output bytes to be written out.

void write(byte[] b)

Writes b.length bytes from the specified byte array to this output stream.

void write(byte[] b, int off, int len)

Writes len bytes from the specified byte array starting at offset off to this output stream.

abstract void write(int b)

Writes the specified byte to this output stream.

Now that we have this common functionality, the next thing to do is see the destinations and the type of data that we can read data from and write data to. The rest of this section discusses the various classes that implement the InputStream and OutputStream interfaces as well as the particulars of their implementations. Figure 12.1 shows the hierarchy of InputStream and OutputStream classes.

Figure 12.1. InputStream and OutputStream Hierarchy.

graphics/12fig01.gif

Predefined Stream Objects

Java has some predefined input and output streams that you are already familiar with:

  • System.in (java.io.BufferedInputStream)

  • System.out (java.io.PrintStream)

  • System.err (java.io.PrintStream)

Because you are already familiar with printing messages out to the screen via a System.out.println() or System.out.print(), you can now work with streams to anything!

Filtered Streams

Java provides a series of what Filter Streams that help you work with streams; specifically, they provide extra functionality to preprocess output before actually writing the data to an output stream or postprocess input after data has been read. To define in more practical terms what this means, consider System.out in light of java.io.OutputStream. Writing text-readable characters to an output stream involves writing a collection of bytes, not a collection of characters, so the PrintStream class is provided that enables you to write characters to it and it writes bytes to the destination.

For example:

 FileOutputStream fos = new FileOutputStream( "myfile.txt" );  PrintStrem ps = new PrintStream( fos );  ps.println( "Here is some text" );  fos.close(); 

The way that filtered streams work is that you pass an output stream to the constructor of a filtered stream, and then you can use the filtered stream instead of the output stream. This enables you to use the filtered stream's methods instead of the original stream's methods; if you choose the correct filtered stream, it will be easier for you to write your data out to the stream.

Java defines the following filtered streams:

  • BufferedInputStream: Provides buffering input operations so that a larger quantity of data can be read into the buffer, and then you can read it out in smaller quantities. This increases efficiency because reading multiple small amounts of data is more expensive than reading one large amount of data.

  • BufferedOutputStream: Provides buffering output operations so that you can write data in small quantities to the buffer, and then, when you are ready, you can flush the buffer to the destination; again this increases efficiency.

  • DataInputStream: Allows you to read binary data of primitive data types. If you want to build a double, you would have to read in 8 bytes and construct it yourself, whereas with this filtered stream you can ask it for a double, and it takes care of reading the 8 bytes and constructing the double.

  • DataOutputStream: Allows you to write binary data as primitive data types; similar to DataInputStream you can write primitive types such as longs and doubles to the stream.

  • PushbackInputStream: Maintains a one-byte pushback buffer so that you can look at the next byte in the input stream without taking the byte. This is good when you want to read to a specific point, and then hand off the stream to another process to handle the next section of data.

  • PrintStream: Provides methods for outputting data textually; enables you to output strings using the familiar print() and println() methods to a binary stream.

The next sections will show examples of using these streams.

Reading From and Writing to Files

Probably the most common use for input and output from most programmers' perspective is reading and writing files. This is the way that you can persist application state between executions. Four classes facility file functions:

  • FileInputStream: Provides an InputStream interface to read data sequentially from a file.

  • FileOutputStream: Provides an OutputStream interface to write data sequentially to a file.

  • RandomAccessFile: Provides a proprietary mechanism to read and write data to and from a file randomly.

  • File: Provides management functionality to learn information about files and directories.

To stay generic enough to read data from any source without knowledge of the source it is best to write your programs around interfaces, not around classes that implement those interfaces. Listing 12.1 is an example that reads a file and outputs it to the standard output as well as another file.

Listing 12.1 FileInputOutputExample.java
 import java.io.InputStream;  import java.io.OutputStream;  import java.io.FileInputStream;  import java.io.FileOutputStream;  public class FileInputOutputExample  {     public static void main( String[] args ) {        try {            InputStream is = new FileInputStream( "in.txt" );           OutputStream os = new FileOutputStream( "out.txt" );           int c;           while( ( c = is.read() ) != -1 ) {          System.out.print( ( char )c );          os.write( c );           }           is.close();           os.close();      }        catch( Exception e ) {           e.printStackTrace();        }     }  } 

Listing 12.1 opens the file in.txt by passing it to the constructor of the FileInputStream class. You can construct a FileInputStream from a String that specifies the filename, a java.io.File object, or a java.io.FileDescriptor (a handle that references the underlying physical file).

Next, it creates an instance of the java.io.FileOutputStream by passing out.txt to its constructor. You can construct a FileOutputStream from a string filename, a java.io.File object, or a java.io.FileDescriptor. Furthermore, if you want to append text to an existing file or simply overwrite the existing file you can specify that in the constructor it overwrites a file if it already exists by default.

Using the java.io.InputStream methods, the sample reads the input stream byte-by-byte and writes it out to the output stream byte-by-byte. It also writes out each byte to the standard output by calling System.out.print(), but note that we must cast the integer byte to a character before writing it out to the stream (otherwise it will output the value as its string representation of the integer).

Finally, the example closes both of the streams; you must close the streams for them to be available to other applications (unless, like in this example program, execution ends, hence, releasing the file handle).

File Class

The java.io.File class provides you information about a file; it answers the following questions:

  • Does the file exist?

  • Is the file read-only or can I write to it?

  • Is the file a file or a directory?

  • If it is a directory, what files are in it?

It also provides you with the following management functionality:

  • Delete a file

  • Rename a file

  • Create a directory

Furthermore, it provides you with a platform-independent abstraction for file separators. The File.separator and File.separatorChar resolve to the proper character based on the operating system you are running on:

  • Windows operating systems use a backslash as the file separator \

  • Unix operating systems use a forward slash as a the file separator /

When you are building filenames be sure to build it as follows:

 String path = "files";  String filename = "myfile.txt"  String qualifiedFilename = path + File.separator + filename; 

Listing 12.2 displays a directory of the root of the drive it is run on and it differentiates between files and directories in the output.

Listing 12.2 FileDirectory.java
 import java.io.File;  public class FileDirectory {     public static void main( String[] args ) {        try {           File f = new File( File.separator );           if( f.isDirectory() ) {              File[] files = f.listFiles();              for( int i=0; i<files.length; i++ ) {                 if( files[ i ].isDirectory() ) {                    System.out.println( "<dir> " + files[ i ].getName() );                 }                 else {                    System.out.println( files[ i ].getName() );                 }              }           }        }        catch( Exception e ) {           e.printStackTrace();        }     }  } 

Listing 12.2 creates a new File by passing the constructor the file separator (\ for Windows or / for Unix); this creates a reference to the root directory of the drive that the application is running on. Next, it provides a sanity check to ensure that this is a directory by calling the isDirectory() method.

If it is a directory, it lists all the files in the directory by calling the listFiles() methods. There are two categories of methods that list files in a directory:

  • listFiles(): Returns an array of java.io.File objects.

  • list(): Returns an array of String objects containing the filenames of the files in the directory.

The reason that we call listFiles() is because one of the operations in the for loop is a check to see whether the element is a directory or a file. If it is a directory, the code prefaces the name with the <dir> string.

Your output will vary from mine, but the following is a sample of what you might see:

 <dir> Apps  AUTOEXEC.BAT  CONFIG.SYS  <dir> Documents and Settings  <dir> My Documents  <dir> Program Files  <dir> projects  <dir> RECYCLER  <dir> System Volume Information  tempfile.tmp  <dir> WINNT 
RandomAccessFile

The java.io.RandomAccessFile class is used for reading files randomly, that is not sequentially from the beginning to the end; you are free to jump around to different locations in the file. It provides both input and output in one class, and it provides methods similar to the java.io.DataInputStream and java.io.DataOutputStream classes to read and write primitive types from and to the file.

To facilitate the random access to the file, the class provides the seek( long pos ) method. This method advances a file pointer from the beginning of the file to the byte and position pos; wherever the file pointer is located is where reads and writes will occur. To summarize, navigating through a file is accomplished by using the following methods:

  • long getFilePointer(): Returns the current location of the file pointer.

  • long length(): Returns the length of the file.

  • seek( long pos ): Moves the file pointer to the specified location (relative to the beginning of the file).

The RandomAccessFile class can open a file for reading or for reading and writing. The file mode is specified in the constructor:

  • RandomAccessFile(File file, String mode)

  • RandomAccessFile(String name, String mode)

Where the mode can be r to specify that the file is to be opened for reading only or rw to specify that the file is to be opened for reading and writing.

Other Byte I/O Classes

The other byte stream classes that Java provides are

  • ByteArrayInputStream: Reads an array of bytes as though it is an InputStream object.

  • SequenceInputStream: Concatenates data from two or more InputStream objects.

  • PipedInputStream: Used for communicating between threads.

  • ByteArrayOutputStream: Sends its output into a byte[] object.

  • PipedOutputStream: Communicates with PipedInputStream to complete thread communication.


       
    Top
     



    Java 2 Primer Plus
    Java 2 Primer Plus
    ISBN: 0672324156
    EAN: 2147483647
    Year: 2001
    Pages: 332

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