by David Wood

Pour On The Java.  Hold The Programming.  Click Here.  Jamba.

"Streaming" in Java

This week we're describing in detail streams in Java so as to lay the groundwork for examples that will be featured in upcoming weeks.

Last week we discussed the basics of the InputStream and OutputStream. We said that streams, technically speaking, stick to the simple model that you read from or write to a file in strict order, from beginning to end. What you saw in the two basic prototype classes for streams should have confirmed this, more or less.

Often, what you want to do with files or streams is actually sensibly done this way. For instance, with the network classes we mentioned, it's not possible (without writing code to keep it all in memory) to skip backwards in what you've read from the stream, and it's not possible (obviously) to skip forwards into what hasn't been sent over the network yet. Streams suit this sort of communications quite well. There are, however, dozens of improvements available (one would hope, right?) over the basic read() and write() methods, and we're going to look at some right now.

Java's suite of I/O stream classes is designed (unlike some other languages one could think of) to provide simple, commonly used tools for working with streams. Overall, we find their coverage of the basics adequate, but C and C++ programmers may mourn the loss of superconfigurable input and output routines like scanf and printf.

Most of the tools we're going to talk about take the form of filters. They sit on top of the basic capabilities of the input and output streams and, rather than actually adding anything new, they simply use the existing read and write methods in new ways.

The first obvious example of this are the DataInputStream and DataOutputStream classes. They're shortcuts, basically; each contains methods for reading or writing types of information corresponding to your basic Java data types--various kinds of numbers, strings, characters, and the like. They make use of the existing routines for reading and writing bytes. Simple as that.

Similar to the DataOutputStream is the PrintStream--except that instead of having separate methods for each kind of output (writeDouble, writeChar, etc.) it has a pair of vastly overloaded methods (print and println) that take as input roughly the same things. If you've been following us closely, you've probably already noticed our use of println and readLine and assumed what they do.

It's easy to apply these specialized streams to basic input and output streams; they're constructed by specifying them.


DataInputStream dis = new DataInputStream(regular_input_stream);

There are a pair of "buffered" input and output streams (named accordingly), providing basic buffering capability with buffers of adjustable size. There's an input stream that keeps track of line numbers as it reads a text file (called, not surprisingly, LineNumberInputStream). PushbackInputStream maintains a small (single character) buffer that allows you to "push a byte back into the stream" once you've read it (useful when you want to peek ahead in what you're reading).

All these classes have in common their inheritance from the FilterInputStream or FilterOutputStream classes. This suggests a system in which you can make your own subclasses of the filters, which you can.

There are other tools as well; for things like byte arrays in memory, or Strings, which you already have random access to, so to speak, there are classes provided here that allow you to have sequential (stream-based) access as well. There's a class that allows you to conduct tokenization on a stream (a topic we'll cover later), and an unusual type of stream that allows for concatenation of multiple streams supplied in an enumeration. And last but definitely not least, there are a pair of "piped streams" that allow for the eminently useful ability to attach a number of filters to one another in series. We'll hopefully have time in the future to cover a bit of this feature as well.

Then there are files. Ordinarily, these are not something that, as an applet designer, you would concern yourself with; however, Java isn't just an applet language--in fact, its applet capabilities are rather an afterthought. This is worth a look.

Files are available from your disk all at once, forwards, backwards, or in any other order you can think of, thanks to the modern wonder of magnetic media. In deference to this situation, the java.io package contains not only an ordinary FileInputStream and FileOutputStream, but also a special class called RandomAccessFile. In the course of normal operation, all these classes can be created with a File object (or just a String containing a filename) to read from or write to a specified file. The stream classes operate much the same way as a basic input or output stream.

The "special" I/O class, on the other hand, contains both input and output capabilities, and is equipped with the functionality of both the DataInputStream and the DataOutputStream. As you may have guessed, however, its primary feature is its ability to seek to anywhere in the file you're working on, without having to read and discard anything you weren't looking for.

Next week we're going to put some of these classes to use. Be sure to stop back and take a look.

Past installments of Java Jolt

http://www.internet.com/