Computer Science Canada

[Tutorial] Streams: java.io fundamentals

Author:  rizzix [ Sun Mar 13, 2005 7:23 pm ]
Post subject:  [Tutorial] Streams: java.io fundamentals

Streams are important in Java, specially when the program relies on the transfer of data to and from an external data source: files, remote hosts, terminals, etc. This tutorial explains the the hierarchy and classes of the java.io package.

Before we start it is important to be aware that the term Input or Reader is associated with reading information and the term Output or Writer is associated with writing information.


Binary Data IO

The lowest level of the classes in java.io, for binary data io, is the InputStream and OutputStream classes.


InputStream (Abstract)
The InputStream class is an abstract class. There is no way you can directly create an object of this class, but you wouldn't need to do so. An object of InputStream is obtained form other source objects, by calling the respective method such as InputStream getInputStream(); Once an object of InputStream is obtained, you may call the following methods to read data from that source:

int read();
Which reads a byte

int read(byte[] b);
Which 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);
Which reads up to len bytes of data from the input stream into an array of bytes

The last two methods return the number of total bytes read into the buffer array.

Calling the void skip(long n); method to skips n number of bytes.
Calling the int available(); method returns the number of bytes avaible to be skipped or read.

Finally after you are done reading data, it is important (at times) to close the stream, do this by calling void close(); I mention "at times" only because in the case of Sockets etc, closing the socket automatically closes the streams. If you are aware that the stream will not be close indirectly by any other means, it is important that you close it directly.


OutputStream (Abstract)
The OutputStream class is also an abstract class. It handles the lower level output operations. The class defines the following methods that you may use to write data to a source:

void write(byte[] b);
Writes all the bytes in the array to stream

void write(byte[] b, int offset, int len);
Writes len bytes of the array from position offset onwards

void write(int b);
Writes the specified byte, b to the output stream

void flush();
If the stream implementation bufferes the data, calling this method will flush the stream, thus ensuring the data is transfered. In the case of OutputStream this method does nothing, since as I mention this class is the lowest level of output as it gets. Data written through an object of this class is directly written to its destination. No data is buffered.

void close();
Closes the stream


BufferedInputStream & BufferedOutputStream
These classes provide no new methods, but extends the lower level IO classes adding buffer capabilities. This means that in the case of BufferedOutputStream the void flush(); method has meaning and can be used. Usually no source objects will return a Buffered stream directly, instead you create a Buffered stream object by wrapping the attained lower level Stream objects. For example:
Java:
BufferedInputStream buffIn = new BufferedInputStream(source.getInputStream());
and similarly
Java:
BufferedOutputStream buffOut = new BufferedOutputStream(source.getOutputStream());



DataInputStream & DataOutputStream
These classes are convenience classes. You usually would interact with these objects rather than lower level Stream objects. To instantiate an object of this class requires a lower level Stream object or a Buffered stream object as its argument to the constructor. After creating an object of this class you may call on one of its many useful methods and including those it inherits from parents, i.e InputStream or OutputStream:

boolean readBoolean(); / void writeBoolean(boolean b);
Reads or Writes a boolean value to/from stream

byte readByte(); / void writeByte(byte b);
Reads or Writes a byte value to/from stream

char readChar(); / void writeChar(char c);
Reads or Writes a char value to/from stream

double readDouble(); / void writeDouble(double d);
Reads or Writes a double value to/from stream

float readFloat(); / void writeFloat(float f);
Reads or Writes a float value to/from stream

int readInt(); / void writeInt(int i);
Reads or Writes a int value to/from stream

int readLong(); / void writeLong(long l);
Reads or Writes a long value to/from stream

int readShort(); / void writeShort(short l);
Reads or Writes a short value to/from stream

String readUTF(); / void writeUTF(String string);
Reads or Writes a string value to/from stream

int readUnsignedByte();
Reads a unsigned byte as int from stream

int readUnsignedShort();
Reads a unsigned short as int from stream

Most of the time a DataInputStream or DataOutputStream object is used over a BufferedInputStream or BufferedOutputStream object, like this:
Java:
DataInputStream dataIn = new DataInputStream(new BufferedInputStream(source.getInputStream()));
and similarly
Java:
DataOutputStream dataOut = new DataOutputStream(new BufferedOutputStream(source.getOutputStream()));
Rarely would you need to write data to a source unbuffered. Just remember whenever you buffer a steam, make sure to call the void flush() on the stream object, after you are done reading/writing to it.


Character Data IO

Reader & Writer (Abstract)
These classes are synonymous to the Input and Output stream classes, i.e they are the lower level io class, but for character data transfer. In almost every way the methods are exactly the same with only the "byte" in the arguments replaced with "char". They are present in Java because of the special "char" datatype. Java characters are unicode characters. These stream classes handle the automatic conversion of UNICODE to ASCII character format. You can infact force the conversion to a specific format of you choice.

Once again the Reader & Writer classes are abstract as well, and behave pretty much just like their Input and Output companions and being abstract they are quite useless.


BufferedReader & BufferedWriter
These class as well behave similarly to the BuffereInputStream and BufferedOutputStream classes, except they work on character data, versus binary (byte) data. The BuferedReader provides a useful new method in addition to those it inherits from Reader: String readLine(); It reads and returns a String object containing characters upto (and not including) the '\n' character from stream.


PrintWriter
As mentioned the BufferedReader provided a very useful method, but on the other hand the BufferedWriter didn't (at least not any useful ones, but keep in mind it did add buffer capabilities). The PrintWriter is the class that adds the missing usefullness to the Writer hierarchy, including internationalization support. You create a PrintWriter object by instantiating it with a BufferedWriter object as the argument to its constructor. After attaining a PrintWriter object you may call of its many useful added methods, including those of its parent class, Writer. I will mot mention the methods in this tutorial, only because there are too many of them (refer to the API instead). But I'm sure you are well aware of the popular println and print methods that are so heavily used in Terminal IO. Well these methods are implemented in the PrintWriter class! There is one consturctor of this class worth mentioning is the constructor that takes a File or String filename object and a String charset object. The charset defines the character format and can be one of "US-ASCII", "ISO-8859-1", "UTF-8", "UTF-16BE", "UTF-16LE" and "UTF-16". These chasets are further explained here: Java Charsets.

NOTE: You cannot choose the charset with a Stream, but only with a File. A stream could be another network computer or terminal. So why can't we choose the charset on a stream, instead only on a file? Actually you can do this. But not through the PrintWriter, instead the PrintStream class.


PrintStream
Similar to the PrintWriter there's a PrintStream class, that extends OutputStream instead of Writer. The PrintStream class also provides the same methods the PrintWriter does, with the "char" in its arguments replaced with "byte". Internally, the PrintStream class converts all data into bytes before transfering it to the destination. The System.out object is a PrintStream object, and hence it has the print and println methods that we so often call.


Other Utility IO Classes

ObjectInputStream & ObjectOutputStream
These Stream classes extend the DataInputStream and DataOutputStream classes respectively and add a couple of useful methods for object Serialization. Since this now leads to Serialization I shall stop. For further information look out for the Serialization tutorial.

Piped Streams and Piped Reader or Writers
These classes I believe also deserve their own tutorial!

InputStreamReader & OutputStreamWriter
These classes are used to encapsulate a Stream object and provide Reader/Writer capabilities. Use this when you want to use, lets say, BufferedReader: now the BufferedReader can only accept a Reader object as the argument to its constructor. Lets assume that we unfortunately have only a Stream object for our use. To work around, we use an InputStreamReader (which extends Reader) as the intermediary object to facilitate such an action. The same can be said for OutputStreamWriter.


: