Java Input and Output (I/O)

Introduction

Input is any information that is needed by your program to complete its execution.  There are many forms that program input may take.  javabook2 programs use graphical components like an InputBox to accept and return the character string that is typed by the user.  You are certainly familiar with programs that are controlled simply by clicking the mouse in a specific area of the screen.  Still other programs, like word processing programs, get some of their input from a file that is stored on the computer's floppy or hard disk drive.  Some programs, like web browsers, get their data from a network connection, while others get data from devices like scanners, digital cameras and microphones.  The possibilities are limited only by computer scientists' imagination.

Output is any information that the program must convey to the user.  The information you see on your computer screen is being output by one or more programs that are currently running on your computer.  When you decide to print a document, a program is told to send some output to the printer.  Any sound that your computer makes is because some program sent output to the speakers on your computer.  The possibilities for program output are also limited only by our imaginations.

Throughout the semester, we have been performing input and output through the use of several of the classes in the javabook2 package provided by the textbook's author.  We used javabook2 classes instead of the standard Java Language classes because they are easier for beginning programmers to understand and use.

One of the reasons the javabook2 classes are easier to use than the standard java classes is because they automatically handle several potential error conditions that may occur when a program needs to get input from the user.  If a user enters letters when a program is expecting numbers, an exception [error] will occur if the program assumes incorrectly that it has a valid integer to use in calculations.  Programs must be written to survive bad input by the user.  One way to do this is to ensure that only valid input is accepted.  javabook2 classes like InputBox ensure that only valid integers are accepted when the programmer calls the getInteger method.

Another reason that we use javabook2 classes instead of the standard library is for simplicity.  Each javabook2 class has a simple constructor or constructors and several intuitive methods for accomplishing common input and output tasks.  For example, to prompt and accept an integer input by the user is very straightforward using classes from the javabook2 package.  You declare and create an InputBox and call the getInteger method.  However, the same action becomes much more complex using only standard java classes.  You may wonder why the standard Java classes are not as easy as the javabook2 classes and the answer is flexibility.  Standard Java classes are designed to be very flexible to support the wide variety of input and output options available now and in the future.  This flexibility comes at the cost of increased complexity.

Console Input

The console window is the [black] window that is automatically launched when you run a program from within CodeWarrior.  Console input is any input that is entered in the console window instead of typing it into a field or dialog box that pops up in a window.  Until now, we have been using an instance of the InputBox class and one of its get methods.  For example, when the getString method was called, a dialog window popped up, and the user was expected to enter information into the blank field.  Whatever the user typed was returned to the program in the form of a String object.

There are other ways to get information.  In many cases, the user must be told that they should enter some information.  This is known as prompting the user.  A user prompt is a line of text that is output to the user that explains what information they should input next.  We can prompt the user by displaying information in a dialog box, a program frame or even the console window.  All programs that require the user to input information while the program is running, must prompt the user for that information in some manner.

When a program is waiting for input at the console, there is a blinking cursor in the console window indicating that the user should type some information.  The user will only know what information to type, if the program describes that information in the form of a user prompt.  (See Console Output for more information on user prompts.)

The use of several of the Java I/O classes are required to successfully receive input that is typed by the user.  The java.io package contains most, if not all, of the classes you will need to use.  Don't worry, you won't need to use all 50+ classes.  But, you will need to learn about and use at least three of them.  All three classes are in the java.io package.  Either use the fully qualified name shown or import the java.io package.

None of these classes has a method as convenient and as error proof as the getString method you've used.  However, the BufferedReader class does have a method called readLine that is almost as good, and does return a line of text as typed by the user.  There are two available constructors for creating a BufferedReader object.  For console input, we will use the one that requires only one argument, an instance of a Reader object.  That means we need to create an instance of the class java.io.Reader.

The InputStreamReader class extends the Reader class.  Here's an analogy to explain extends:  All Robins are Birds, but not all Birds are Robins.  Therefore, Robin extends Bird.  If someone needs a Bird, then a Robin can be used.  This means that any instance of the InputStreamReader class can be used whenever an instance of the Reader class is required.  The inheritance hierarchy of the InputStreamReader class shows that it extends the Reader class because the Reader class is higher in the hierarchy tree.

We will create an instance of the InputStreamReader class to be used to create the BufferedReader that we want.  We choose to create an InputStreamReader instead of a Reader object because we want to get input from an InputStream.  Later, you will see that File I/O will require the use of a different type of Reader object.

What do we need to have in order to create an instance of the InputStreamReader class?  According to the Java API, we will need an InputStream object.  Oh, now I see why we needed all three classes!  Luckily, part of our work is done for us.  The System class in the java.lang package automatically creates an InputStream object that is connected to the keyboard.  It is called System.in and is part of the java.lang package.

We will use the System.in object to create an instance of the InputStreamReader class and then use that object to create an instance of the BufferedReader class.  That's not so bad, but still not as easy as creating an InputBox, wouldn't you agree? 

Steps for console based user input:

  1. Use the System.in object to create an InputStreamReader object.
  2. Use the InputStreamReader object to create a BufferedReader object.
  3. Display a prompt to the user for the desired data.
  4. Use the BufferedReader object to read a line of text from the user.
  5. Do something interesting with the input received from the user.

Would you like to see some code?  I thought so.  Here it is:

	// 1. Create an InputStreamReader using the standard input stream
	InputStreamReader isr = new InputStreamReader( System.in );

	// 2. Create a BufferedReader using the InputStreamReader created.
	BufferedReader stdin = new BufferedReader( isr );

	// 3. Don't forget to prompt the user
	System.out.print( "Type some data for the program: " );

	// 4. Use the BufferedReader to read a line of text from the user.
	String input = stdin.readLine();

	// 5. Now, you can do anything with the input string that you need to.
	// Like, output it to the user.
	System.out.println( "input = " + input );

That's a lot of code for one line of input.  Is there a shorter way?

Yes, most Java programmers combine steps 1 & 2 and create only one instance of the BufferedReader for use throughout their entire program.  All keyboard operations will use that single shared BufferedReader object.  The code below is placed with other class data members and is not inside any method.

	// 1&2. Create a single shared BufferedReader for keyboard input.
	private static BufferedReader stdin = new BufferedReader( 
		new InputStreamReader( System.in ) );

I added the above code to my program and I get compiler errors!

Did you remember to import the java.io classes?  The BufferedReader (and other I/O classes) are not in the standard java.lang package.  You must import the java.io package to declare and create instances of any of the Java I/O classes.   Add the import java.io.*; statement to your list of other import statements.

You will also have to inform the compiler that you are calling a method that may cause a checked exception to occur.  Add the phrase throws IOException to the header of any method that calls stdin.readLine().  You will also need to add this clause to any method that calls your method that calls readLine.  Here's a complete program example that prompts the user for input and then repeats that data to the console window:

	import java.io.*;  // needed for BufferedReader, InputStreamReader, etc.

	/** A Java program that demonstrates console based input and output. */
	public class MyConsoleIO 
	{
		// Create a single shared BufferedReader for keyboard input
		private static BufferedReader stdin = 
			new BufferedReader( new InputStreamReader( System.in ) );

		// Program execution starts here
		public static void main ( String [] args ) throws IOException
		{
			// Prompt the user
			System.out.print( "Type some data for the program: " );


			// Read a line of text from the user.
			String input = stdin.readLine();

			// Display the input back to the user.
			System.out.println( "input = " + input );

		} // end main method

	} // end MyConsoleIO class

Integer input

Getting data from the user isn't so hard after all.  But, it does require some additional work.  There is even more work to do, if you want to get an integer (or other numeric value) from the user.  If the user types in "123", that will be still be returned as a String object by the readLine method of BufferedReader.  You will need to parse [convert] the String into an int value if you wish to store it in an int variable or data member.  Here's how:

  1. Get a String of characters that is in an integer format, eg.  "123".

    String input = stdin.readLine();  // from console input example above.
  2. Use the Integer class to parse the string of characters into an integer. 

    int number = Integer.parseInt( input );  // converts a String into an int value

    The Integer class contains conversion methods for changing String data into int values and vice versa.  The Integer class is one of several wrapper classes that are defined in the standard Java API.  Wrapper classes have class methods for parsing and are also used when you need to store a primitive value as an object.  More on this use later in the course.

    In our case, we needed to convert a String into an int value.  The parseInt method of the Integer class performs this action.  Be sure to review the Integer class javadoc for more information about this and other methods of the Integer class.

What if the user types letters instead of digits?

The parseInt method declares that it may throw a NumberFormatException.  If the user types any string of characters that can't be parsed into an int value, a NumberFormatException will be thrown and the program will crash.  That can't be a good thing!  But, there is something that you as the programmer can do to keep your program from crashing.  You can catch the NumberFormatException.  If you don't know what an exception is, read about them in Java Exceptions.  There is an example on catching a NumberFormatException.

Console Output

We have used System.out.print(...) and System.out.println(...) statements as an alternative to creating and displaying an OutputBox or MessageBox for displaying simple text messages to the user.  This is an important output alternative, since javabook2 classes and other graphic user interface (GUI) objects are not readily available in some programming environments.  You may of course write your own GUI classes if they're not available, but that is beyond the scope of this course.  It is much more likely that you will simply use the available output options of the programming environment that you are working in. 

Most programming languages have the ability to display a string of characters to the screen or some other standard display device.  We call this console output because the string of characters appears in a console window.  The System.out object is an instance of the PrintStream class, which is a type of Stream.

Streams

A Stream object is used to store information needed to connect a computer program to an input or output device.  Just like there is a Reader object that adds functionality to input streams, there is a Printer object that adds functionality to output streams.  The PrintStream class extends the Printer class and contains definitions for all of the versions of the print and println methods that we use to display information like user prompts or results of calculations, etc.

Console output in Java is very easy because the print and println methods will work with any type of data.   There is a separate version of each of these methods in the PrintStream class so that this is possible.  There is also a version of the print and println methods that will print information for any object.  But, how did we get a PrintStream object in the first place?

The java.lang.System class creates three different I/O streams automatically for us when our application begins execution.  Each of these streams is public and static so that we can access them directly without having to create an instance of the System class.  We have already used the InputStream object named System.in in the discussion on console input.  The other two stream objects are named System.out and System.err.  Each of these objects is an instance of the PrintStream class and is available for use in displaying information to the computer screen.

For example, if the following variables are defined,

    int x = 3;
    double rate = 5.5;
    boolean playing = true;
    String phrase = "The winner is ";

they can all be printed using print or println as follows:

    System.out.print( "x = " + x + " rate = " );
    System.out.println( rate );
    System.out.println( "playing = " + playing );
    System.out.println( phrase + "Deb" );

We can also print other types of data, including other objects, using the print and println methods.  The following code fragment shows the command syntax for printing a Wanderer object.

    Wanderer wanderer1 = new Wanderer( "Wilma", Color.orange );
    System.out.println( wanderer1 );

In this case, the program prints out some cryptic information about the Wanderer.  It is the class name, an @ symbol and the hexidecimal representation of the hashcode.  The output looks like the following for the first Wanderer object I created.

    Wanderer@13fac

Each object created has its own hashcode that can be used to distinguish it from other objects.  However, hashcodes are not very readable for most users, so there is a way for the programmer to redefine what information is printed.  The information that is displayed when an object is printed using the print method is defined by an instance method named toString.  Every class has a version of the toString method already defined that returns the information as described above.  All classes inherit this method from the java.lang.Object class.

To redefine the toString method, you override the default version by defining a method with the same visibility and method signature as the inherited version of the method.  The toString method of my Wanderer class can be overridden as follows:

    /**
     *  Returns a string representing this
     *  instance of the Wanderer class.
     */
    public String toString()
    {
        String coords = "(" + myLoc.getX() + ","
                            + myLoc.getY() + ")";

        return myName + " is at " + coords;
    }

Now, when the print or println method is used to print a Wanderer object, the new version of the toString method will be called instead of the version defined in the Object class.  The String that is printed by the println method will look something like this:

    Wilma is at (11,3)

Each class can and should override the toString method to return a String of characters that is more descriptive of the object than the default version provided by the Object class.  This method can be called by any method that needs a String that describes the object.

The print or println methods of the PrintStream class should be adequate to produce most screen output.  The only other type of output we will cover in this course is file output.  Other output devices require more specialized output objects and won't be covered in this course.

File Input

As mentioned above, data can be read from a variety of different sources, including data files stored on devices such as hard disk drives and floppy drives.  The file will need to be opened and a BufferedReader will be attached to the file object.  The process is actually very similar to the console input example above.  The difference is that the BufferedReader will be created from a FileReader object instead of an InputStreamReader object.  Your textbook describes file input when the file stores individual bytes.  This section focuses on inputting characters rather than data bytes.

The discussion and examples in this document explain the procedure when the file to be read is a text file that has valid ASCII characters to represent the data.  Here are two new Java I/O classes to review:

Here's a code fragment to illustrate reading a text file.  The readLine method may cause an IOException which is a checked exception, so be sure to catch the exception or add the throws IOException to the method header.  See the Java Exceptions web page for more information on handling checked exceptions.

    System.out.print( "Enter the filename: " );   // Prompt the user for a file name
    String fileName = stdin.readLine();           // get a file name from the user
    java.io.File file = java.io.new File( fileName ); // create a File object

    if ( file.exists() )                          // check that the file exists
    {                                             // before trying to create a
                                                  // BufferedReader
        // Create a BufferedReader from the file
        java.io.BufferedReader inFile = new java.io.BufferedReader(
            new java.io.FileReader( file ) );

        // For each line in the file, read in the line and display it with the line number
        int lineNum = 0;

        // Compare the results of calling the readLine method to null
        // to determine if you are at the end of the file.
        String line = inFile.readLine();
        while ( line != null )
        {
            System.out.println( ++lineNum + ": " + line );
            line = inFile.readLine();
        }

        // Close the buffered reader input stream attached to the file
        inFile.close();
    }

File Input Hints

String Tokenizer

When you are reading data into your program from a text file, you may need to interpret certain parts of each line differently.  This is especially true if you need your program to create objects from text data contained in a file.  Individual data items can be placed on different lines of the input data file, but this creates very long files.  It is much more common to place all of the data items for one object on the same line and separate each item with some special character, called a delimiter.  In this type of data file, each line of the file represents one record or one object.  The example data file "student_scores.txt" shown below, contains data for three students and three exams scores for each student.

Each line of input is interpreted as a string of characters for the name, that is followed by three integers for each object created.  The delimiter character is the ':' in the above data file.

The process of splitting a line of text into different parts is known as tokenizing, and each piece is a token.  The standard Java library includes a class called, StringTokenizer that makes this process much more convenient for Java programmers.  Without this class, you would need to use nested repetition and selection statements to process each character one at a time and determine if it is part of the name or one of the exam scores and then store it accordingly.  Java programmers simply need to learn how to construct a StringTokenizer object and what methods are available.  Refer to the StringTokenizer javadoc for more detailed information on methods other than those presented here.

A StringTokenizer object is created from a String object.  You can access each token similar to the way in which you accessed each Wanderer using the BallTeam object in Assignment #1.  Use the hasMoreTokens method to determine if there are any remaining tokens to process.  Use the nextToken method to return the next token as a String object.  There is also a countTokens method that returns the number of remaining tokens available for a particular instance of the StringTokenizer class.

The StringTokenizer class is in the java.util package, so you must fully qualify the name as java.util.StringTokenizer or import the class.  Here is a code fragment that reads the file shown above and computes the average score for each student listed.  If you call nextToken when there are no tokens remaining, a java.util.NoSuchElementException will occur.

        // Make sure that the file student_scores.txt exists and has 
        // valid data records.  Otherwise, exceptions will occur.
        File gradeFile = new File( "student_scores.txt" );
        if ( gradeFile.exists() )
        {
            // Create the buffered reader for reading the file
            BufferedReader inFile = new BufferedReader( new FileReader( gradeFile ) );

            // Get the first line of the file
            String line = inFile.readLine();

            // If line is not end of file continue
            while ( line != null ) 
            {
                // Create a StringTokenizer with a colon sign as a delimiter
                java.util.StringTokenizer st = 
                    new java.util.StringTokenizer( line, ":" );
  
                // Display the content of the first token
                System.out.print( "   Name: " + st.nextToken() );

                // Display the total number of tokens remaining this string
                int numScores = st.countTokens();
            
                // Initialize the sum to zero
                int sum = 0;

                // Get each score, add it to the sum and print it
                for ( int i=1; i <= numScores; i++ )
                {
                    int score = Integer.parseInt( st.nextToken() );
                    sum += score;
                }

                // Display the average score for this student
                System.out.println( " average = " + sum/numScores );
  
                // Read next line of the file
                line = inFile.readLine();

            } // end while not at end of file
            
            // Close the BufferedReader
            inFile.close();
            
        } // end if the grade file doesn't exist

File Output

Writing data to a file is similar to writing data to the screen.  You will open a file for writing and then print to that file any data that you would like to store there.  You must remember to close the file or risk having some data not be written and saved to the file.  We will use each of these classes.

When you intend to write data to a file, you should consider what the appropriate action to take is, if the file already exists.  The safest option is to ask the user what to do, and then allow the user to choose overwrite the file, choose a different filename or cancel the operation.  The example shown below assumes that the file opened by the FileWriter object will be overwritten if it already exists.  If you do not want to overwrite the file if it already exists, then you must create and test a File object first.  The exists method of the File class will return true if the file already exists.

        // Create a FileWriter attached to a file named "out.txt".
        // The second parameter sets whether or not new data
        // will be appended to the end of the file or the beginning.
        // false means that this file will be overwritten.
        java.io.FileWriter fw = new java.io.FileWriter( "out.txt", false );
	
        // Create a PrintWriter that automatically flushes data
        // to the output file whenever the println method is used.
        java.io.PrintWriter pw = new java.io.PrintWriter( fw, true );

        // Buffer some data to write to the file (doesn't actually write until flush)
        pw.print( "Some test data that will be written when flush is called.");
		
        // Flush all buffered data to the file.
        pw.flush();

        // Write some data and automatically flush it to the file.
        pw.println( data );

        // Close the PrintWriter for added safety.
        pw.close();

The nice thing about using a PrintWriter object is that you are already familiar with the print and println methods that are defined for all PrintWriter objects.  By choosing the constructor that accepts a String for the filename and a boolean value, we are able to set the PrintWriter object so that it will always flush the data buffer on all calls to println.  This is safer, but less efficient.  It is more efficient to wait until the buffer is full before writing to disk.  In that case, the flush method must be called or the file must be closed to flush the remaining data from the buffer.

Conclusion

Input and output using the standard Java library of classes is somewhat more complex than using javabook2 classes.  By using and experimenting with each of the techniques presented here, you will be able to perform some of the most common input and output operations in your Java programs.

ConsoleIO.java has additional examples of console based input and output, written by Jim Skrentny.

FileIO.java has an additional examples of file based input and output, written by Jim Skrentny.


Copyright 2002 Deb Deppeler.  Last update on 11/21/2002 08:26 AM