Reading and Writing Data Using Byte Streams

   

There are many different types of streams within the Java API. Each type of stream within the Java API is represented by a different Java class or interface. The InputStream and OutputStream classes represent the simplest of the byte stream classes. These two classes provide general streaming capabilities for byte data. Java uses these base classes to derive other classes that are more specifically oriented toward a certain type of input or output. You can find all the various stream classes in the java.io package. Here are a few of the available streams for byte data:

  • BufferedInputStream ” A basic buffered input stream

  • DataInputStream ” An input stream for reading primitive data types

  • FileInputStream ” An input stream used for basic file input

  • ByteArrayInputStream ” An input stream whose source is a byte array

  • StringBufferInputStream ” An input stream whose source is a string

  • LineNumberInputStream ” An input stream that supports line numbers

  • PushbackInputStream ” An input stream that allows a byte to be pushed back onto the stream after the byte is read

  • PipedInputStream ” An input stream used for inter-thread communication

  • SequenceInputStream ” An input stream that combines two other input streams

  • PrintStream ” An output stream for displaying text

  • BufferedOutputStream ” A basic buffered output stream

  • DataOutputStream ” An output stream for writing primitive data types

  • FileOutputStream ” An output stream used for basic file output

  • FilterInputStream ” An abstract input stream used to add new behaviors to existing input stream classes

  • FilterOutputStream ” An abstract output stream used to add new behaviors to existing output stream classes

  • ByteArrayOutputStream ” An output stream whose destination is a byte array

  • PipedOutputStream ” An output stream used for inter-thread communication.

There are too many stream classes to be covered thoroughly in a single chapter. An entire book could be written on Java I/O alone. For that reason, this chapter covers the most useful of the stream classes. Before getting into the specialized byte stream classes, here's a brief introduction of the abstract base byte stream classes that describes the behavior that all the byte stream classes inherit.

The InputStream Class

The InputStream class represents the basic input stream. As such, it defines a set of methods that all input streams need. Table 21.1 lists these methods , without their parameters.

Table 21.1. Methods of the InputStream Class
Method Description
read() Reads data into the stream
skip() Skips over bytes in the stream
available() Returns the number of bytes immediately available in the stream
mark() Marks a position in the stream
reset() Returns to the marked position in the stream
markSupported() Returns a boolean value indicating whether the stream supports marking and resetting
close() Closes the stream

Troubleshooting Tip

If you are getting an incorrect number when using the available method. See "Using the available() Method" in the "Troubleshooting" section at the end of the chapter.


The read method is overloaded in the class, providing three methods for reading data from the stream. The methods'signatures look like this:

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

The most basic method for getting data from any InputStream object is the read method:

 public abstract int read() throws IOException 

The read method reads a single byte from the input stream and returns it. This method performs what is known as a blocking read , which means that it waits for data if there is none available. So, when a datasource doesn't have any data to be read yet, the method will wait until a byte becomes available before returning. One example of this situation is when the stream is on a network and the next byte of data might not have arrived yet. You want to be careful with this situation, however, because it can cause similar problems to the synchronization problems discussed back in Chapter 11, "Threads," if you are not careful.

See "Threads,"

When the stream reaches the end of a file, this method returns a negative 1.

Note that to be able to return 8 bits of data (a byte) and still have “1 only occur when the stream is at an end, the values are actually returned as if they were generated from an unsigned byte. That is, if you write a “1 into a stream, it will actually get read back in as an integer with a value of 255. Fortunately, casting that int back to a byte will return the value to “1.

Note

This read method is the most important because it is the method that actually grabs data from the native source. All the other methods use this one to perform their work.


This read() method

 public int read(byte[] bytes) throws IOException 

fills an array with bytes read from the stream and returns the number of bytes read. It is possible for this method to read fewer bytes than the array can hold, because there might not be enough bytes in the stream to fill it. When the stream reaches the end of the file, the read method also returns a negative one ( “1). You will always receive all the bytes in the stream before you hit the end of the file. In other words, if 50 bytes are left in the stream and you ask to read 100 bytes, this method returns the 50 bytes, and then the next time it is called, it returns “1, if the stream is at an end.

This version of the read() method

 public int read(byte[] bytes, int offset, int length) throws IOException 

fills an array starting at position offset with up to length bytes from the stream. It returns either the number of bytes read or “1 for end of file.

The read method always blocks (it sits and waits without returning) when there is no data available. To avoid blocking, you might need to ask ahead of time exactly how many bytes you can safely read without blocking. The available method returns the number of bytes that can be read with the stream being blocked:

 public int available() throws IOException 

You can skip over data in a stream by passing the skip method the number of bytes you want to skip over:

 public long skip(long n) 

The skip method actually uses the read method to skip over bytes, so it will block under the same circumstances as read. It returns the number of bytes it skipped or “1 if it hits the end of file.

Some input streams enable you to place a bookmark of sorts at a point so that you can return to that location later. The markSupported method returns true if the stream supports marking:

 public boolean markSupported() 

The mark method marks the current position in the stream, so you can back up to it later:

 public synchronized void mark(int readLimit) 

The readLimit parameter in the mark method sets the maximum number of bytes that can be read from the stream before the mark is no longer set. In other words, you must tell the stream how many bytes it should let you read before it forgets about the mark. Some streams might need to allocate memory to support marking, and this parameter tells them how big to make their arrays.

If you set a mark in a stream, you can reposition the stream back to the mark by calling the reset method:

 public synchronized void reset() throws IOException 

After you are done with a stream, it's very important to always close the stream. You can do this with the close method like this:

 public void close() throws IOException 

Although streams usually get closed at garbage collection time, it's good practice and much safer to close all your open resources when you are finished with them. On the majority of operating systems, the number of files you can have open at one time is limited. Therefore, you should close your streams when you are finished with them to free up system resources immediately without waiting for garbage collection.

The OutputStream Class

The counterpart to InputStream is the OutputStream class, which provides the basic functionality for all output streams. Table 21.2 lists the methods defined in the OutputStream class, along with their descriptions.

Table 21.2. Methods of the OutputStream Class
Method Description
write() Writes data to the stream
flush() Forces any buffered output to be written
close() Closes the stream

Rather than being a source of data like the input stream, an output stream is a recipient of data. The most basic method of an OutputStream object is the write method.

 public abstract void write(int b) throws IOException 

writes a single byte of data to an output stream.

 public void write(byte[] bytes) throws IOException 

writes the entire contents of the bytes array to the output stream. This version of the write() method

 public void write(byte[] bytes, int offset, int length) throws IOException 

writes length bytes from the bytes array, starting at position offset.

Depending on the type of stream, you might need to occasionally flush the stream if you need to be sure that the data written on the stream has been delivered. Flushing a stream does not destroy any information in the stream; it just makes sure that any data stored in internal buffers is written out onto whatever device the stream might be connected to. To flush an output stream, call the flush method:

 public void flush() throws IOException 

As with the input streams, you should close output streams when you are done with them by calling the close method.

Reading and Writing Byte Arrays

There are many situations in which you have to read or write a byte array to a file, memory, or even a network socket. The ByteArrayInputStream and ByteArrayOutputStream classes are designed to make it easy to read and write byte arrays, respectively. Both classes inherit from the base stream classes, but are designed specifically for byte arrays.

ByteArrayInputStream is designed for holding an internal byte array and then streaming in the bytes using the read method on the class. The class has two constructors:

 ByteArrayStream(byte[] buf ) ByteArrayStream(byte[] buf, int offset, int length) 

ByteArrayOutputStream is specifically designed for written bytes. The class's internal buffer can grow as the bytes are written into it. The data can be obtained by using the toByteArray() or toString() method. The ByteArrayOutputStream class also has two constructors:

 ByteArrayOutputStream() ByteArrayOutputStream(int size) 

The size argument in the second constructor sets up a buffer with a specified size, in bytes.

Reading and Writing Files

The FileInputStream and FileOutputStream classes are designed to read and write bytes from a file in the file system. Both classes have several constructors that can use a filename represented by a String, a java.io.File argument, or a FileDescriptor. After an instance is created, you can read in the bytes from the file using the standard read method.

Note

If you have binary data that you need to read or write from a file, a FileInputStream or FileOutputStream class is probably a good one to use. However, if you need to read or write text to or from a file, a better choice is one of the reader or writer classes that you'll see later in this chapter.


Stream Buffering

The BufferedInputStream and BufferedOutputStream classes offer byte streams that have internal buffering capabilities. This allows you to programmatically mark a certain position in the buffer stream using the mark method and then return to that position later using the reset method.

Buffered streams help speed up your programs by reducing the number of reads and writes on system resources. Suppose you have a program that is writing one byte at a time. You might not want each write call to go out to the operating system, especially if it is writing to a disk. Instead, you want the bytes to be accumulated into big blocks and written out in bulk. The BufferedInputStream and BufferedOutputStream classes provide this functionality. When you create them, you can provide a buffer size:

 public BufferedInputStream(InputStream in) public BufferedInputStream(InputStream in, int bufferSize) public BufferedOutputStream(OutputStream out) public BufferedOutputStream(OutputStream out, int bufferSize) 

The BufferedInputStream class tries to read as much data into its buffer as possible in a single read call; the BufferedOutputStream class calls the write method only when its buffer fills up, or when flush is called.

Filtering Streams

One of the most powerful aspects of streams is that you can chain one stream to the end of another. The basic input stream, for example, only provides a read method for reading bytes. If you want to read strings and integers, you can attach a special data input stream to a general input stream and suddenly have methods for reading strings, integers, and even floats. The FilterInputStream and FilterOutputStream classes provide the capability to chain streams together. The FilterInputStream and FilterOutputStream classes don't provide any new methods, however. Their big contribution is that they are "connected" to another stream. The constructors for the FilterInputStream and FilterOutputStream classes take InputStream and OutputStream objects as parameters:

 public FilterInputStream(InputStream in) public FilterOutputStream(OutputStream out) 

Because these classes are themselves instances of InputStream and OutputStream, they can be used as parameters to constructors to other filters, enabling you to create long chains of input and output filters.

The DataInputStream class, discussed later in this chapter, is a useful filter that enables you to read strings, integers, and other simple types from an input stream. In addition, the LineNumberInputStream filter automatically counts lines as you read input. You can chain these filters together to read data while counting the lines:

 LineNumberInputStream lineCount = new LineNumberInputStream( System.in ); DataInputStream dataIn = new DataInputStream( lineCount ); 

Print Streams

The PrintStream class and its only subclass, LogStream, provide the capability to print or log data more conveniently than other stream classes. You also can set these classes to be automatically flushed after data is written. PrintStream never throws an exception. Rather it sets a internal flag that can be tested with the checkError method.

   


Special Edition Using Java 2 Standard Edition
Special Edition Using Java 2, Standard Edition (Special Edition Using...)
ISBN: 0789724685
EAN: 2147483647
Year: 1999
Pages: 353

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