INPUT USING THE INOUT STREAM READER CLASS AND "WRAPPER" CLASSES

NOTE: This set of www pages is not the set of www pages for the curent version of COMP101. The pages are from a previous version that, at the request of students, I have kept on line.


CONTENTS

1. Overview
2. Input in Java
2.1. Exceptions
2.2. An example program (Hello World 2)
3. Wrapper classes
3.1 parseInt Method
 
3.2 Integer Constructor
3.3 "Value" Methods
3.4 toString Methods
3.5 MIN_VALUE and MAX_VALUE Values
4. Refinement of the input process
4.1. Alternative using "value" methods



1. OVERVIEW

The inpit mechanism described here was that use with Java 1.4 and is not required with respect to Java 5.




2. INPUT IN JAVA 1.4

CLASS DIAGRAM SHOWING API CLASSES ASSOCIATED WITH INPUT

Figure 1: Class diagram showing Java 1.4 API classes associated with input (remember that open arrows indicate inheritance, while closed arrows indicate association)

To facilitate input Java provides a "stream" class called BufferedReader to input characters. A class diagram indicating the relationship between this class and some other significant classes (at least in the context of input) is given in Figure 1. Note that all the classes, except the root class, shown in Figure 1 are contained (amongst others) in the java.io package.

The BufferedReader class contains (amongst other things) the methods readLine and read. This first is used to input a line of text (a string), while the second is used to input a single character. In both cases a carriage return is required to signal to the Java interpreter that the input is ready. The input is placed in a temporary storage location called a buffer (something that "buffers" the program from the keyboard in a manner analogous to the way that sets of railway station buffers might be said to "buffer" trains from platforms!); hence the class name BufferedReader.

To use this method we must address two issues:

  1. The methods readLine and read are instance methods, thus we require an instance of the BufferedReader class, i.e. a BufferedReader object.
  2. We must tell Java what the source of the input stream will be, for example the keyboard, secondary storage, Etc.

To create an instance of a class we require an appropriate constructor. Inspection of the BufferedReader class shows that a constructor of the form:

BufferedReader(Reader in);

is provided. The formal parameter to this constructor, in, is an instance of the class (type) Reader which is a superclass of the class BufferedReader (see Figure 1). This argument is used to tell Java what the source will be. Thus in addition to a BufferedReader object we also need a Reader object. Alternatively from Figure 1 we can see that we can use an InputStreamReader object or a BufferedReader object instead because both are sub-classes of Reader. Remember that through the principle of inheritance an instance of a sub-class is also an instance of all its super classes, therefore we can substitute an inputStreamReader or a bufferedReader object for a reader object. We have already considered the BufferedReader class, any further consideration would simply result in us "going round in circles". However, inspection of the inputStreamReader class indicates the presence of a constructor that has the form:

InputStreamReader(InputStream in)
 

So now we need an instance of the class InputStream! Because input from the keyboard is such a common requirement Java provides an appropriate predefined InputStream object, called in, for keyboard input. This is a class field (keyword static) contained in the class System contained in the package java.lang (which also contains the object out, the instance of the PrintStream class discussed earlier in the context of output) Remember that the package jaba.lang is always compiled into every Java program. Thus we can create an InputStreamReader object as follows:

InputStreamReader input = 
    new InputStreamReader(System.in)

Note that:

We have called the instance input (we could have called it anything we liked), we say that the object input is an instance of of (or is of the type InputStreamReader.
Remember that the keyword new tells the Java compiler to set aside appropriate storage (on the heap) for all the instance fields associated with the class InputStreamReader.
Remember also that when we refer to a class field from outside its class we must link it to its class using a membership operator, hence System.in.

Now that we have an InputStreamReader object we can create an instance of the class BufferedReader. We will call it keyboardInput:

BufferedReader keyboardInput = 
    new BufferedReader(input);

Alternatively we can combine the two statements as follows:

BufferedReader keyboardInput = 
    new BufferedReader(new 
    InputStreamReader(System.in));

In the second case we create an anonymous object of type InputStreamReader (i.e. one without a name) and consequently our code becomes more succinct but less readable or understandable. Thus, for reasons of readability and understandability we will, for the time being, be adopting the first approach. We can now invoke the readLine method:

keyboardInput.readLine();

This is not quite the end of the story! because although readLine has no formal parameters it returns a string (read from the keyboard) which must be assigned to an appropriately defined variable (data item). We say that the readLine method has a return type of String. Thus we need a variable of type String in which to store the input. A string variable called name would be declared as follows:

String name;

We can now "capture" keyboard input thus:

name = keyboardInput.readLine();

Note: the type String is a predefined compound type. The nature of the type String will be discussed further at a later date.

In Figure 2 the input class diagram given in Figure 1 is combined with the output class diagram presented previously.

CLASS DIAGRAM SHOWING API CLASSES ASSOCIATED WITH INPUT AND
	OUTPUT

Figure 2: Class diagram showing Java 1.4 API classes associated with input and output



2.1 Exceptions

One source of (run time) error is when the user of a program inputs an exceptional value; i.e. data that is not of the expected type, or (in the case of numeric input) data that exceeds the predefined range of values specified for a numeric type. When a Java method detects an exception we say that it throws the exception. If you examine (say) the BufferedReader class you will notice that the readLine() method throws an IOException. The precise nature of this exception will be explained later, but for the time being we will simply accept that where any predefined method throws an exception you should append a "throw clause" to the signature of the method from which the methods such as readLine() are called. Thus:

public static void main(String[] args) throws IOException

Exceptions are classes the same as any other class; they are arranged in a class hierarchy descending from the class Exception, which is itself a sub class of the class Throwable.



2.2 An example Program (Hello World 2)

Using the above we can extend the Hello World program described earlier so that it takes some input as indicated by the Java code presented in Table 1.

Note that, in the above code, we have expressly imported the java.io package (unlike java.lang this is not included automatically) using an import statement. The '*' symbol is a "wild card" symbol indicating all the classes in the named package. The java compiler will then "compile in" those classes that are referred to our class definition. We could have specified particular classes. For example:

// HELLO WORLD PROGRAM 2
// Frans Coenen
// Monday 15 January 1999
// The University of Liverpool, UK

// Import packages containing predefined classes

import java.io.*;

class HelloWorld2 {

    // ------------------- FIELDS ------------------------ 
    
    // Create BufferedReader class instance

    public static InputStreamReader input         = 
    		new InputStreamReader(System.in);
    public static BufferedReader    keyboardInput = 
    		new BufferedReader(input);
    
    // ------------------ METHODS -----------------------
    
    /* Main method  */

    public static void main(String[] args) throws IOException {
        String name;
	
        System.out.print("What is your name? ");
        name = keyboardInput.readLine();
	
        System.out.print("\nHello " + name );
        System.out.println(" - Congratulations on writing " + 
		"your first Java program which features " +
		"some input!\n\n");
        }
    }

Table 1: "Hello Word" version 2

import java.io.BufferedReader
import java.io.InputStreamReader

However the effect is the same; there is no performance degradation associated with mentioning the whole package for import hence most programmers use the * wild card character when writing import statements. hence:

import java.io.*

A more sophisticated output mechanism similar to that described for input above can also be implemented.




3. WRAPPER CLASSES

In the above example we have shown how we can input a string into a Java program using the readLine method. Input is always in the form of a string; so what if we wish to input (say) an integer or a float? To do this we must convert the string into the appropriate format. Because this is a very common requirement Java provides a mechanism whereby this may be achieved. We use something called a wrapper class. Each of the primitive types char, int, long, float, double, etc. has a corresponding wrapper class associated with it: Character, Integer, Long, Float, Double etc. (note the upper case letter for the first character of each identifier in keeping with the Java class naming convention). These classes are designed to "wrap" the primitive types into a class.

CLASS DIAGRAM SHOWING API CLASSES ASSOCIATED WITH THE INTEGER WRAPPER CLASS

Figure 3: Class diagram showing API classes associated with the Integer wrapper class

In the following subsections we will examine some of the features of these wrapper classes by considering part of the contents of one such class, the Integer class (Figure 3).



3.1 parseInt Method

Each wrapper class has a "parse" method that takes a string as its argument and returns the appropriate primitive data item. Parse methods play an important role with respect to numeric input and will be the standard mechanism which we will use in these pages.

Parse methods are static methods, i.e. they are class methods, and thus to make use of them we must link them to a class name. For example in the case of the parseInt (see Figure 3) method this would be used as follows:

Integer.parseInt(s)

where s is the string to be parsed.

The example application program presented in Table 2 illustrates the process of inputting an integer. If (after compilation) we run this program we will be requested for some input, which will then be echoed to the screen:

$ java IntegerInputApp
Input an integer 23
The value is 23         

Try inputting a very large number such as 1000000000000. This will generate an exception because this number is larger than the maximum defined for the integer type:

 
java.lang.NumberFormatException: 
	1000000000000
    at java.lang.Integer.
	parseInt(Integer.java)
    at java.lang.Integer.
	< init >(Integer.java)
    at IntegerInputApp.
	main(IntegerInputApp.java:35) 

Note the NumberFormatException (do not worry at this stage if you do not understand all of the resulting output.)



3.2 Integer Constructor

Wrapper classes of course include constructors so that we can create instances of such classes. Thus Integer class contains (amongst other things) a constructor that creates an instance of the class Integer that represents the value given by a string argument, i.e. it "wraps-up" an int data item into an instance of the class Integer:

public Integer(String s) throws 
	NumberFormatException;

(Note that this throws an exception called NumberFormatException).



3.3 "Value" Methods

Wrapper classes also include a "value" method to assign the value "wrapped-up" in an instance of the class to a suitably defined data item. In the case of theInteger class this is the method:

public int intValue();

which assigns the value "wrapped" up in the class to a data item of type int.



3.4 toString Methods

Each wrapper class also includes a toString method which converts its the value "wrapped" up in the class to a string. This is most commonly used automatically by the Java interpreter when ever it discovers a concatenation operator (+) in an output statement (e.g. print or println).

Remember that the concatenation operator joins strings, however the operands for the operator do not necessarily have to be strings. Where this occurs the Java interpreter will automatically invoke the appropriate toString method --- there is one in each wrapper class. Of course to simply print out only (say) an integer or a double we can use the appropriate print or println defined in the PrintStream and PrintWriter Classes. If we look in these classes we will see that there are print and println methods for each of the primitive types.



3.5 MIN_VALUE and MAX_VALUE Values

Finally it is worth noting the wrapper classes for discrete types (such as the Integer wrapper class) also includes two class constants called MIN_VALUE and MAX_VALUE (see Figure 3). These contain the maximum and minimum value that an discrete type can take, and are useful when checking inputs to ensure that they do not go out of range (more on this later).


// INTEGER INPUT
// Frans Coenen
// Thursday 31 July 2003
// The University of Liverpool, UK

// Import packages containing predefined classes

import java.io.*;

class IntegerInputApp {
    // Create BufferedReader class instance

    static InputStreamReader input         = new InputStreamReader(System.in);
    static BufferedReader    keyboardInput = new BufferedReader(input);
    
    /* Main method  */

    public static void main(String[] args) throws IOException {	
	
	// Invite input
	
	System.out.print("Input an integer ");
	
	// Read in input as a string.
	
	String stringInteger = keyboardInput.readLine();
	
	// Convert string input to data item of type Integer
	
	int inputInt = Integer.parseInt(stringInteger); 
	
	// Output the result
	
	System.out.println("The value is " + inputInt);
	}
    }

Table 2: Integer input example applications program




4. REFINEMENT OF THE INPUT PROCESS

The above example includes a number of unnecessary data items, namely stringInteger and intInstance. We can use anonymous data item and rewrite the above as shown in Table 3. This provides a much more succinct encoding and is the approach we will commit to using in the following examples.

// INTEGER INPUT
// Frans Coenen
// Thursday 31 Jukly 2003
// The University of Liverpool, UK

// Import packages containing predefined classes

import java.io.*;

class IntegerInputApp {
    // Create BufferedReader class instance

    static InputStreamReader input         = new InputStreamReader(System.in);
    static BufferedReader    keyboardInput = new BufferedReader(input);
    
    /* Main method  */

    public static void main(String[] args) throws IOException 
        {	
	
	// Invite input
	
	System.out.print("Input an integer ");
	
	// Read in input as a string and convert to data item of type 
	// integer all on one line.
	
	int inputInt = Integer.parseInt(keyboardInput.readLine()); 
	
	// Output the result
	
	System.out.println("The value is " + inputInt);
	}
    }

Table 3: Refined integer input example program



4.1 Alternative using "value" methods

In the code presented in Table 3, instead of using the parseInt(s) class method we can, alternatively, use the intValue() instance method. Using this approach requires the following steps:

  1. Read in the integer in the form of a string and store it in a string variable.
  2. Create an instance of the class Integer and assign a value to it using the Integer(String s) constructor.
  3. Assign the instance value to an appropriately defined integer variable using the intValue method.

The application program presented in Table 4 implements this.

// INTEGER INPUT
// Frans Coenen
// Monday 1 March 1999
// The University of Liverpool, UK

// Import packages containing predefined classes

import java.io.*;

class IntegerInputApp {

    // ------------------- FIELDS ------------------------ 
    
    // Create BufferedReader class instance

    public static InputStreamReader input         = 
    		new InputStreamReader(System.in);
    public static BufferedReader    keyboardInput = 
    		new BufferedReader(input);
    
    // ------------------ METHODS ------------------------
    
    /* Main method  */

    public static void main(String[] args) throws IOException {
	
	// Invite input
	
	System.out.print("Input an integer ");
	
	// Read in input as a string, convert input into an instance 
	// of type Integer and assign the value associate with the 
	// Integer instance to the variable inputInt 
	
	int inputInt = new Integer(keyboardInput.readLine()).intValue(); 
	
	// Output the result
	
	System.out.println("The value is " + inputInt);
	}
    }

Table 4: Alternative main method for "Refined integer input example" program given in Table 3

There are "value" methods for all the basic types. Whether to use a "value" instance method, or a "parse" class method is up to individuals.




Created and maintained by Frans Coenen. Last updated 10 February 2015