Section 4.3 A Command-Line Interface
A command-line interface is perhaps the simplest, and most old-fashioned, way to design the interaction between a user and a program. According to this approach, user input is taken from the keyboard, and the program's output is displayed on some kind of console (Figure 4.3.1).
The command-line approach might also be called console interface. In the early days of computers, before we had graphics-based computer monitors capable of displaying multiple windows, the console was the entire computer display. For today's computers the console might be a window provided by your programming environment, as in Figure 4.3.2.
In Chapter 3 we described how to use the
System.out.println() methods to output strings to the console. That takes care of the output side of command-line interface. The more challenging task is managing the input-side of the interface.
In Java, input and output is handled by objects that are called streams. You can think of a stream as a kind of pipe through which data flow (Figure 4.3.3). An input stream carries data from some kind of input device, such as a keyboard or network connection or a file, to the program's main memory. An output stream carries data from the program's memory to some kind of output device, such as a printer or a file.
Each Java program has three standard streams available to it at startup:
System.in is a predefined input stream that is typically associated with the keyboard (Figure 4.3.3). That is, it carries data from the keyboard to the program.
System.err are both output streams typically associated with the console. They both carry data from the program to the console. The difference is simply that
System.out is used for normal program output and
System.err is used to output error messages.
Subsection 4.3.1 Using a
BufferedReader to Input Strings from the Keyboard
We will use a
BufferedReader object to handle data input from the keyboard. As its name implies, the
BufferedReader class performs buffered input. A buffer is a portion of main memory where input is held until it is needed by the program. Using a buffer between the keyboard and the program allows you to use the Backspace key to delete a character. When you hit the Enter key, any characters that you deleted will be ignored when the program retrieves characters from the input buffer. If the user's input were not buffered in this way, it would contain every keystroke, including the Backspaces, and then it would be up to the program to eliminate the characters that were supposed to be deleted.
Figure 4.3.4 provides a UML diagram of the
BufferedReader class and shows its relationship to other the classes that will be used for keyboard input . Note that along with
BufferedReader is one of several subclasses of the
Reader class. As the diagram shows,
BufferedReader has two important methods. Its constructor method takes a
Reader parameter, which means that when we create a
BufferedReader we must provide it with a reference to some kind of
Reader object. To perform keyboard input, we want to provide a reference to an object that can read
System.in, the standard input stream. As the figure shows,
InputStreamReader has a constructor that allows it to read an
InputStream. Therefore, to construct a
BufferedReader that will read
System.in we use the following statement:
BufferedReader input = new BufferedReader(new InputStreamReader (System.in));
In this statement we are actually creating two objects. We first create an
InputStreamReader, giving it a reference to
System.in. We then pass that object to a
BufferedReader. The result is a cooperation between two objects that enables us to do buffered reading of the keyboard.
By creating a
BufferedReader in this way, whenever we use its
readLine() method, it will read a line of characters from the keyboard. For example, having created a
input, the following code segment will read one line of input and assign it to the
String variable named
String inputString = input.readLine();
When the program encounters the
readLine() expression, it will wait for the user to hit the Enter key. It will then input whatever the user typed, minus any characters that were Backspaced over, into the
Principle 4.3.5. Keyboard Input.
BufferedReader.readLine() method allows the user to backspace over errors during keyboard input.
Subsection 4.3.2 Inputting Numbers from the Keyboard
As the previous section showed, we can use a
BufferedReader object to input
String s from the keyboard. In Java, all keyboard input is represented as
String s. However, what if we want to input numbers? The answer is that we have to extract numbers from the input strings. To do this, Java provides us two special classes, known as wrapper classes:
A wrapper class contains methods for converting primitive data into objects and for converting data from one type to another. The
Integer class contains the
parseInt() method, which extracts an
int from its
String argument. For example, in the following usage, the string "55" is converted into the number 55:
int m = Integer.parseInt("55");
Double class contains the
parseDouble() method, which extracts a
double value from its parameter. In this example, the number 55.2 is extracted from the string "55.2":
double num = Double.parseDouble("55.2");
If we are writing a program that requires us to input numbers from the keyboard, then assuming we have created a
BufferedReader object named
input, we can use these methods in combination with the
readLine() method, to input and process numbers. For example, this code segment calculates a runner's race pace:
String inputString = new String(); System.out.println("How many total miles did you run? "); inputString = input.readLine(); // Input a String} double miles = Double.parseDouble(inputString); // Convert System.out.println("How many minutes did it take you? "); inputString = input.readLine(); // Input another String double minutes = Double.parseDouble(inString); // Convert System.out.println("Your average pace was " + minutes/miles + " minutes per mile");
Notice how we included prompts in this example so that the user knows what type of input is expected. Designing appropriate prompts is an important aspect of designing a good user interface.
Principle 4.3.6. EFFECTIVE DESIGN:Prompting.
In a well-designed user interface, prompts should be used to guide the user through the input process.
Subsection 4.3.3 Designing a Keyboard Reader Class
Now that we have introduced the library classes and methods that we will use for command-line input, lets design a class to encapsulate these functions. We want a class that will use a
BufferedReader to read any kind of data—strings, integers, or real numbers—from keyboard. We also want this class to hide some of the messy details involved in performing keyboard input.
Figure 4.3.7 presents the design of
KeyboardReader class. Note that instances of this class will use a
BufferedReader object to perform the actual keyboard input. That's why we need a private instance variable of type
BufferedReader. The constructor method will create a
BufferedReader, which will then be used whenever a read operation is requested. Note that the
KeyboardReader() has five public methods. The
getKeyboardInput() method returns a
String. This is the method we will call when we just want to get the string that the user typed from the keyboard. The
getKeyboardInteger() method returns an
int value. This is the method we will call when we want an integer from the keyboard. Similarly, the
getKeyboardDouble() method returns a
double. This is the method we will call when we want to input a floating point value from the keyboard. Finally, the
display() methods will be used to perform two other important tasks of a user interface: that of prompting the user and that of displaying the program's output.
The following code segment illustrates how we will use a
KeyboardReader object to input an integer:
KeyboardReader cmdline = new KeyboardReader(); int m = cmdline.getKeyboardInteger();
All we need to do is create an instance of the
KeyboardReader and ask it to get an integer for us. This greatly simplifies the work we would have to do when we want to perform keyboard input.
Note that Figure 4.3.7 lists a private method named
readKeyboard() in the
KeyboardReader class. This is the method that does the actual work of reading data from the keyboard. Because it is private, it can only be called by the other methods in
KeyboardReader. It cannot be called by other classes. The reason we make it private is to hide it, and the messy details of performing keyboard input, from other classes.
One of those messy details is the fact that whenever I/O is performed, it is possible for things to go wrong. The possibility of errors occurring applies to all forms of I/O, not just keyboard I/O. For example, when a program is trying to read a file, the file might be missing. Or when trying to download a web page, the Internet connection might malfunction.
Because these types of external errors are possible, Java requires that whenever a program performs certain types of I/O, it must watch out for certain kinds of error conditions, known as exceptions. Exceptions are covered in Chapter 11, so we will not attempt to cover them here. Instead, we will design the
readKeyboard() method to take care of this detail for us.
Principle 4.3.8. Exceptions.
Java I/O methods require that programs check for certain error conditions during input.
Listing 4.3.9 gives the full implementation (for now) of the
Lets go through it line by line. The first thing to notice is the use of the
import statement. Recall that importing a Java package enables us to refer to elements in the package by their short names (
BufferedReader), rather than by their fully qualified names (
Next notice how we create a
BufferedReader object in the
reader = new BufferedReader (new InputStreamReader (System.in));
reader object will persist as long as our
KeyboardReader object exists and can be used for all subsequent input operations.
Next notice the definition of the
readKeyboard() method. It calls the inherited
readLine() method to input a line from the keyboard and then it returns the line. Note, however, how the call to the
readLine() method is embedded in a
try...catch block. This is one way to handle the possibility that an exception might occur during the input operation. Java requires that our program do something to address the possibility of an I/O exception, and as we will learn in Chapter 11, there are other designs that we might have used here. The primary advantage of doing it this way is that we can hide this language detail from the rest of the program. The rest of the program—and any other programs that use the
KeyboardReader class—will not have to worry about this exception issue. They can just ask the
KeyboardReader to get them a string or an integer and it will deliver the goods.
Next, notice how the public input methods are defined. The
getKeyboardInput() method just returns the line that it gets by calling
getKeyboardInteger() method also calls
readKeyboard(), but instead of just returning the line, it extracts an integer from it and returns the integer. The
getKeyboardDouble() method works the same way.
Finally, notice how the public output methods are defined. Both the
display() methods take a single
String parameter and do exactly the same thing–they merely print their string. So why do we have two methods when one will suffice? The answer is that these methods encapsulate important and distinct user-interface functions—prompting the user and displaying output—that just happen to be implemented in exactly the same way in this case. As we will see when we design our GUI interface, we will use completely different objects to prompt the user and display output. So, despite their similarities, it is important that we distinguish the task of prompting the user from the more general task of displaying output.
Subsection 4.3.4 Designing a Command-Line Interface
Now that we have defined a special class for performing keyboard input, we now show how it can be used as a user interface in cooperation with the other objects that make up a program. As described in Figure 4.2.1, the user interface will serve as an intermediary between the user and some type of computational object. Although our command-line interface should work with any application, no matter how complex, we begin with a very simple computational problem. This will allow us to focus on the user interface.
Let's design a program that prompts the user for his or her name and then says hello. Thus, the program's I/O should look like this:
Hi, please input your name here > Kim Hi Kim, nice to meet you.
In the design we use there will be two primary objects involved. One will serve as the user interface. This will be our
KeyboardReader. A second object will serve as the computational object. In this case it will “compute” an appropriate greeting. It will serve contain the
main() method and will encapsulate the algorithm for this application. It will use a
KeyboardReader to handle its I/O needs.
The main advantage of this division of labor is that it enables us to use the
KeyboardReader, as is, with virtually any Java application. Moreover, despite its simplicity, our computational object in this example can serve as a template for future programs.
Principle 4.3.10. EFFECTIVE DESIGN:Modularity.
By designing the user interface as a self-contained module, we can use it with just about any application.
Figure 4.3.11 provides the details the design we wish to implement. Note that
GreeterApp contains an instance variable for a
KeyboardReader. This will enable it to use the
KeyboardReader whenever it needs to perform keyboard input. By giving
main() method, we allow it to be the main class for our application. Its
run() method will contain the algorithm that controls the application, and its
greet() method will handle the task of greeting the user.
The full implementation of the
GreeterApp class is shown in Listing 4.3.12. It begins by declaring an instance
variable for the
KeyboardReader, which is instantiated in the constructor method. This gives
GreeterApp a way to refer directly to the user interface whenever it needs keyboard input. The
run() method encapsulates the application's algorithm. Notice how it uses the
KeyboardReader to prompt the user, to input the user's name, and then to display the greeting. Finally, the
main() method serves to create an instance of the computational object and calls its
To re-cap, we have designed a simple command-line interface that can be used, with minor changes, for virtually any programming task in subsequent chapters. Before moving on, it may be helpful to touch on some of the important object-oriented principles that went into our design.
Divide-and-conquer: We see the usefulness of dividing a program into separate objects, one to handle the computations required by the application, and one to handle the user interface.
Encapsulation: The classes we designed encapsulate just the information and behavior that is necessary to perform their specific roles.
Information hiding: We use a private method to hide certain messy implementation details from other parts of the program.
Generality and Extensibility: We have developed a design that is general enough that it can be extended to other applications.
Exercises Self-Study Exercises
1. HiLow Guessing Game.
Math class has a static method that will generate a random number between 0 and 0.99999999—that is, between 0 and 1, not including 1. By using simple arithmetic, we can generate random numbers between any two values. For example, the following statement assigns a random integer between 1 and 100 to the variable:
secretNumber = 1 + (int)(Math.random() * 100);