This tutorial is all about how to do non-GUI I/O in Java. It is essentially an out-of-class lecture.
In Java, input and output is defined in terms of an abstract concept called "stream". A stream is a sequence of data; conceptually it is like a list, but not necessarily with an end.
We use distinct types of streams for input and output.
java.io
package provides a
large number of classes to perform stream I/O.
This tutorial introduces you to some commonly used classes and methods,
plus the StreamTokenizer
class used to parse input
streams.
InputStream
and OutputStream
classes
Input and output in Java of byte streams are handled through subclasses of the
java.io.InputStream
and java.io.OutputStream
classes. These classes are abstractions
that Java provides for dealing with reading and writing information
sequentially to and from anything you want:
e.g., a disk, a string buffer, an enumeration, or an array of bytes.
Below is the UML diagram for a few commonly used concrete subclasses of
InputStream
.
Note that the StreamTokenizer
class shown in the diagram is
not a subclass of InputStream
.
StreamTokenizer
is a utility class used to parse the
stream of input, and will be discussed below.
The first step in using a concrete input stream class is to specify
its source for its constructor.
For instance, to read the contents of a file into a String
,
you create a new FileInputStream
, giving the
filename in the constructor, as in the following:
import java.io.*; ... String buffer = new String(); // read bytes until eof try { // FileInputStream constructor takes // a String, a File object, or a FileDescriptor object . FileInputStream infile = new FileInputStream("myfile.txt"); for (int i = infile.read(); i != -1; i = infile.read()) { buffer += (char) i; } infile.close(); } catch(IOException e) { System.err.println("Could not read from myfile.txt"); } catch(FileNotFoundException e) { System.err.println("myfile.txt not found"); }
The available()
method
returns the number of bytes that can be read from the stream.
The close()
method closes the link between the program
and the source of the data.
After you close the stream, you cannot read from it
anymore until you open it up again.
The reset()
method ensures that the next input operation
on this stream begins at the top of the file again.
Several read()
methods are available.
Each datum is read as an int
or byte
;
to convert it to a char
, you have to cast it.
Output streams are very similar.
Below is the UML diagram for a few commonly used concrete subclasses of
OutputStream
.
Reader
and Writer
classes
Input and output for characters streams are handled through subclasses of the
java.io.Reader
and java.io.Writer
classes.
These classes are abstractions that Java
provides for dealing with reading and writing character data.
Below are the UML diagrams for a few commonly used
Reader
and Writer
classes:
StreamTokenizer
class
Many times when reading an input stream of characters,
we need to see if what we are reading is a word or a number.
This process is called "tokenizing".
java.io.StreamTokenizer
is a class that can do simple
tokenization.
Exercise:
TestStreamTokenizer.java is a
sample program showing how to use it.
Copy it and the file
~comp212/public_html/01-spring/labs/09/input.txt
to your local directory and test it.
This is similar to one piece of the
RPN calculator project.
System
class
The java.lang.System
class is a useful class containing useful
static members, such as
System.out.println("Hello world!");You've seen this a lot, but what's really going on? As we've mentioned before,
out
is a static
PrintStream
field of the System
class.
A PrintStream
is a grandchild of OutputStream
in the Java class hierarchy.
It has methods to print text a line at a time, rathan than a
character at a time.
This field is initialized when a program starts to what is known as
standard output or stdout.
Stdout is usually the monitor screen, but you can also send stdout
to a file at runtime by redirecting it from the Unix command line.
E.g., contrast
% java MyClasswith
% java MyClass > outputfile
There is also a System.in
class, similarly initialized
to the InputStream
standard input or stdin.
Stdin represents the keyboard, but we can similarly change
System.in
by redirecting the input:
% java MyClass < inputfile
There is also a System.err
PrintStream standard error or stderr.
Stderr also represents the monitor screen, so what's the point?
This is intended to be the output stream used for error messages,
and you can redirect it independently of stdout.
% java MyClass > outputfile % java MyClass >& output_and_errorfileor even (in the
bash
shell)
% java MyClass 1> outputfile 2> errorfile
NOTE:
System.out
and System.err
are the
only PrintStream
objects you should use.
The PrintStream
is deprecated, i.e., a historical
hold-over that my go away.
For output character streams, you should use PrintWriter
instead (as described above).
So how would you read in an arbitrary number of floating point numbers from the keyboard? Here's a sample.
With many programs, you want to be able to set options about how your
program should behave at runtime.
For instance, your Java compiler needs to know what files to compile,
or you might have a variety of features that you want your users to
have access to.
(Check out the manual page for the Unix command ls
.
There's a vast amount of features regarding how to display
your data, what to display, etc.)
You can handle this in several ways, but
a common technique is to use command-line arguments.
Remember how all your main functions for your programs have to have the same signature?
public static void main(String[] argv) {...}The array of
String
s is where your program receives
command-line arguments.
When using the UNIX java command,
everything after the class name is included in argv
.
Exercise: The program Echo.java just echos back the command-line arguments to stdout. Look at it, run it, and make sure you know how to give it some command-line arguments.