Lecture 9

Input and Output

Input and Output allow data to enter and leave the program.  In Java, both input and output are considered to be a stream (sequence) of byte values.

An input stream is a sequence of bytes that is entering the program.  For example, the keyboard usually provides a stream of bytes representing characters typed by the user .

An output stream is a sequence of bytes leaving the program.  The bytes of an output stream can be displayed to the user, or can be captured in an output file.

It is important to distinguish input and output streams from the parameters and return values of methods.  Input and output streams have to do with data entering and leaving the program as a whole.  Parameters have to do with data entering an invocation of a particular method, and a return value has to do with data leaving (being returned from) an invocation of a particular method.  Parameter values and return values stay within the program.

Standard Input and Output Streams

All Java programs have access to several standard input and output streams:

Stream Kind Purpose
System.in Input Standard input
System.out Output Standard output
System.err Output Standard error

You have already seen System.out in the form of statements which print output using System.out.println.  You can think of messages sent to System.out as being like a virtual printer: they appear as lines of characters that are displayed to the user.

Data read from System.in comes from the keyboard (by default).  Reading from System.in is thus a way to read information typed by the user.

By convention, System.err is used for displaying error messages to the user.

InputStream and OutputStream

The InputStream and OutputStream classes reside in the java.io package.  Instances of these classes represent input and output streams, respectively.  An InputStream object may be used to read bytes of data from an input stream, and an OutputStream object may be used to write bytes of data to an output stream.

The standard input and output streams are instances of these two classes that are accessed through static fields in the java.lang.System class.

System.in is a static field whose type is the class java.io.InputStream.

System.out and System.err are static fields whose type is java.io.PrintStream.  On the surface, that seems strange, because all output streams are instances of java.io.OutputStream.  However, there is no contradiction, because both of these statements are true:

  1. System.out and System.err are instances of of java.io.PrintStream
  2. System.out and System.err are instances of of java.io.OutputStream

The reason that both statements are true is because the PrintStream class inherits from the OutputStream class.  Inheritance is a feature of object-oriented languages like Java.  It allows one class, the derived class, to "inherit" the features of another class, the base class.  We will cover inheritance in a future lecture.  For now, it is sufficient to understand that any object that is an instance of PrintStream is also an instance of OutputStream, and has all of the capabilities of an OutputStream object.

One of the capabilities that the PrintStream adds to OutputStream is being able to print characters in addition to bytes.  This is an important ability, since any textual output, such as strings, needs to be in the form of characters.  You have already seen the mechanism by which a PrintStream outputs strings and characters: the print and println methods.  For example, the statement

System.out.println("Hello");
can be broken down as follows:
System.out.println("Hello");

The green part of the statement means "the static field called out in the class System".  This part of the statement selects the "standard" output stream, which is an instance of the PrintStream class.

The blue part of the statement means "the println method that is defined in the PrintStream class".

The magenta part specifies the argument value being passed to the println method.  In this case it's a string containing the characters "Hello".

The net result of this statement is that a sequence of byte values representing the characters "Hello" is sent to the standard output stream.

The Scanner class

The declared type of the System.in static field is InputStream.  Instances of InputStream provide the capability to read byte values from an input stream.  However, this isn't terribly useful if we want to read textual input like characters and strings from the user or from a file.  It also does not help if we want to read numeric input.

The java.util.Scanner class provides the capability to read tokens of input from an InputStream object.  A token is a sequence of characters delimited in the input stream by separator characters.  Although a Scanner object can use any desired characters as separators, the default is to use whitespace characters as separators.  Whitespace characters include the space character and the tab character, as well as the newline character used to end lines of input or output.

As an example, let's say that a scanner is reading from an input stream containing the following sequence of characters:

Taxation without representation is tyranny.

This sequence of input characters will result in the following sequence of tokens:

Taxation
without
representation
is
tyranny.

Each sequence of non-separator characters is combined into a single token.  The separator characters are ignored.

Here is a complete program to read tokens of input from System.in and print them as strings to System.out:

import java.util.Scanner;

public class ScannerDemo {
  public static void main(String[] args) {
    Scanner keyboard = new Scanner(System.in);

    while (keyboard.hasNext()) {
      String token = keyboard.next();

      System.out.println(token);
    }
  }
}

Note that a scanner object is created by invoking a constructor and passing it an InputStream: in this case, System.in.

Scanners and Numeric Input

A Scanner object can also read numeric input.  For example, a scanner can automatically convert the sequence of characters "1" "2" "1" into the integer value 121 by calling the nextInt method.  The nextDouble method can read a sequence of characters and convert it to a double value.

Here is part of a program that could be used to calculate interest on money in a bank account:

Scanner keyboard = new Scanner(System.in);
System.out.print("Balance: ");
double balance = keyboard.nextDouble();

System.out.println("Interest rate: ");
double interestRate = keyboard.nextDouble();

double newBalance = balance + (balance * interestRate);
System.out.println("After interest, the balance is " + newBalance);

One concern with using a Scanner to read tokens is how erronenous input is handled.  For example, the program might call nextDouble at a time when the next characters waiting to be read from the scanner are "XYZ": this sequence is not a valid representation of a number, and thus cannot be converted into a double value.

When the available input does not match the kind of value requested from the scanner, an InputMismatchException occurs.  We haven't yet covered the topic of exceptions, but what you should know at the moment is that exceptions will automatically terminate the program unless you take special steps to handle the exception.

If you try to read any kind of token when the sequence of characters available from the input stream has been exhausted, a NoSuchElementException will occur.  Again, unless you take special steps to handle the execption, it will terminate the program.

Seeing if Input is Available

One way that you can avoid getting exceptions when using a Scanner is to invoke methods that check to see whether the desired kind of input is available.  For example, the hasNextDouble can be called to check whether or not characters representing a double value are available to be read from the scanner.  Here is a safe version of the interest-calculating program:

Scanner keyboard = new Scanner(System.in);
System.out.print("Balance: ");
if (!keyboard.hasNextDouble()) {
  System.err.println("Need a number");
  return;
}
double balance = keyboard.nextDouble();

System.out.println("Interest rate: ");
if (!keyboard.hasNextDouble()) {
  System.err.println("Need a number");
  return;
}
double interestRate = keyboard.nextDouble();

double newBalance = balance + (balance * interestRate);
System.out.println("After interest, the balance is " + newBalance);

Validation code like this makes the program more complex, so in many respects it is better to let the exceptions occur when invalid input is detected.  We will learn how to handle exceptions later in the course.

Here is a partial list of the methods supporting the various kinds of input that can be read using a Scanner.  Check the API documentation for the Scanner class for details.

Check availability Read value
hasNext next
hasNextShort nextShort
hasNextInt nextInt
hasNextLong nextLong
hasNextFloat nextFloat
hasNextDouble nextDouble