Low-level and High-level Byte Streams
There are low-level streams, which read and write data in bytes or characters, and high-level streams, which read data in bytes from a low-level stream and return formatted data to the caller (and vice versa).
1. Low-Level Streams
A low-level input stream reads data and returns it in bytes, and a low-level output stream accepts data as bytes and writes the output in bytes. Two examples of low-level streams are represented by the classes FileInputStream and FileOutputStream, which are subclasses of InputStream and OutputStream, respectively.
1.1 FileInputStream Class
The FileInputStream class is designed for reading image files as it reads a stream of raw bytes. When we instantiate the FileInputStream class, an opaque connection is created to the specified file, and this connection is represented by an object of the FileDescriptor class. We can specify the file with which we want to connect our program in one of the following three ways:
- Specify a File object that represents an actual file in the file system.
- Specify a path (in String format) to an actual file in the file system.
- Specify a FileDescriptor object that represents a connection to an actual file in the file system.
The constructors of the FileInputStream class corresponding to these three options are described below:
Constructor Summary |
FileInputStream(File file) |
FileInputStream(FileDescriptor fdObj) |
FileInputStream(String name) |
Note that we can create a stream connection only with a regular file, and not with a directory. This makes sense, because we can only write bytes or characters to a file and not to a directory. If the specified file is a directory rather than a regular file, or it does not exist, or it cannot be read for some reason, a FileNotFoundException is thrown. The following are the signatures of the FileInputStream constructors with the File or String parameter:
public FileInputStream(…) throws FileNotFoundException;
After we create an instance of the FileInputStream class, we can use it to perform operations such as reading a single byte, an array of bytes, or a part of an array of bytes by invoking the following methods:
- int read() throws IOException: Returns the next byte of data or 1 if the end of the file is reached
- int read(byte[] bytes) throws IOException: Reads bytes.length number of bytes from the stream into an array, and returns the number of bytes read, or -1 if the end of the file is reached
- int read(byte[] bytes, int offset, int len) throws IOException: Reads up to a total of
len bytes (starting from offset) into an array, and returns the number of bytes read, or –1 if the end of the file is reached
- void close(): Closes the input stream and releases any system resources assigned to the stream
These entire read methods block until an input is available. So, the FileInputStream reads 8-bit bytes. The counterpart of FileInputStream to write the bytes is FileOutputStream.
1.2 FileOutputStream Class
The FileOutputStream class is meant for writing streams of raw bytes into files, such as image files. When we instantiate the FileOutputStream class, an opaque connection is created to the specified file, and this connection is represented by an object of the FileDescriptor class. Just as with FileInputStream, we can specify the file with which we want to create a connection either by specifying the file directly or by specifying the FileDescriptor object.
The corresponding commonly used constructors for FileOutputStream are described below:
Constructor Summary |
FileOutputStream(File file) |
FileOutputStream(File file, boolean append) |
FileOutputStream(FileDescriptor fdObj) |
FileOutputStream(String name) |
FileOutputStream(String name, boolean append) |
Note that we can create a stream connection only with a regular file, and not with a directory. A FileNotFoundException is thrown if the specified file is a directory rather than a regular file, if it does not exist and cannot be created, or if it cannot be accessed to write to for some reason. The signatures of the FileOutputStream constructors with the File or String parameter are shown here:
public FileOutputStream(…) throws FileNotFoundException;
After we create an instance of the FileOutputStream class, we can use it to perform operations such as writing a single byte, an array of bytes, or a part of an array of bytes by invoking the following methods:
- void write(int b) throws IOException: Writes the passed-in byte to the stream
- void write(byte[] bytes) throws IOException: Writes bytes.length number of bytes from the passed-in array to the stream
- void write(byte[] bytes, int offset, int len) throws IOException: Writes up to a total
of len bytes (starting from offset) from the passed-in array to the stream
- void close(): Closes the output stream and releases any system resources assigned to the stream
2. High-Level Streams
When the unit of information we are interested in is a high-level data type such as a float, an int, or a String, and we don’t want to deal with bytes directly, we can work with high-level streams. However, these streams do not directly read from or write to an I/O device; rather, they are attached to low-level streams, which in turn are attached to the I/O device. In other words, data is always read from an input device or written to an output device by a low-level stream. Two examples of high-level streams are DataInputStream and DataOutputStream, discussed next.
2.1 DataInputStream Class
Java offers the DataInputStream class to enable an application to read primitive data types from an underlying input stream in a machine-independent way. However, as Figure 9.7 shows, it does not read directly from an input device such as a file, but rather is attached to a low-level stream that reads bytes from the input device and processes the bytes into values of desired high-level data types.
Figure 9: DataInputStream is attached to FileInputStream, which in turn is attached to the file
The constructor for the DataInputStream class is:
public DataInputStream(InputStream in)
Note that because the FileInputStream class extends InputStream, we can also invoke the constructor by passing in an instance of FileInputStream rather than InputStream. This is how we attach a low-level stream with a high-level stream. We can use DataInputStream to read any primitive data type by invoking the corresponding method from the following list:
- boolean readBoolean() throws IOException
- byte readByte() throws IOException
- char readChar()throws IOException
- double readDouble() throws IOException
- float readFloat() throws IOException
- int readInt() throws IOException
- long readLong() throws IOException
- short readShort() throws IOException
The counterpart of DataInputStream is DataOutputStream, which we can use in our program to write primitive data types to a file.
2.2 DataOutputStream Class
Java offers the DataOutputStream class to enable an application to write primitive data types to an underlying output stream in a machine-independent way. However, it does not write directly to an output device such as file, but rather is attached to a low-level stream that writes bytes to the output device.
The constructor for the DataOutputStream class is
public DataOutputStream(OutputStream out)
Note that the FileOutputStream class extends OutputStream. Therefore, we can also invoke the constructor by passing in an instance of FileOutputStream rather than OutputStream. We can use DataOutputStream to write any primitive data type by invoking the corresponding method from the following list:
- void writeBoolean(boolean b) throws IOException
- void writeByte(byte b) throws IOException
- void writeBytes(String s) throws IOException
- void writeChar(int c) throws IOException
- void writeChars(String s) throws IOException
- void writeDouble(double d) throws IOException
- void writeFloat(float f) throws IOException
- void writeInt(int i) throws IOException
- void writeLong(long l) throws IOException
- void writeShort(short s) throws IOException
The writeBytes(…) and writeChars(…) methods write out strings as sequences of bytes and chars, respectively, to the underlying output streams.
All the streams discussed in this section are collectively called byte streams because, regardless of which stream we are using, the data is always being written to the output device and being read from an input device in units of bytes. These streams are better suited to read binary files such as image files and primitive data types. We can also read text files (characters) in their 8-bit byte representation.
DataInputStream class vs. DataOutputStream class :-
DataInputStream | Vs. | DataOutputStream |
This class extends FilterInputStream class and implements DataInput interface. | (1) | This class extends FilterOutputStream class and implements the DataOutput interface. |
Methods contained in DataInput are:- · readShort() · readInt() · readLong() · readBoolean() · readFloat() · readDouble() · readLine() · readChar() · readUTF() | (2) | Methods contained in DataOutput are:- · writeShort() · writeInt() · writeLong() · writeBoolean() · writeFloat() · writeDouble() · writeBytes() · writeChar() · writeUTF() |
In the next section, we will talk about the Low-level and High-level Character Stream Classes.