Skip to main content

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).

Figure 4.3.1. A command-line user interface.

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.

Figure 4.3.2. The Java console window.

In Chapter 3 we described how to use the System.out.print() and 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.

Figure 4.3.3. Input and output streams.

Each Java program has three standard streams available to it at startup: System.in, System.out, and System.err. 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.out and 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. The BufferedReader class.

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 InputStreamReader, 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 BufferedReader named input, the following code segment will read one line of input and assign it to the String variable named inputString.

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 String variable.

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: Integer and Double.

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");

Similarly, the 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.

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. Design of the KeyboardReader class.

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 prompt() and 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.

ListingĀ 4.3.9 gives the full implementation (for now) of the KeyboardReader class.

  import java.io.*;
  public class KeyboardReader
  {   private BufferedReader reader;
      public KeyboardReader() {
          reader = new BufferedReader
          (new InputStreamReader(System.in));
  }
  public String getKeyboardInput()
  {   return readKeyboard();
  }
  public int getKeyboardInteger()
  {   return Integer.parseInt(readKeyboard());
  }
  public double getKeyboardDouble()
  {   return Double.parseDouble(readKeyboard());
  }
  public void prompt(String s)
  {   System.out.print(s);
  }
  public void display(String s)
  {   System.out.print(s);
  }
  private String readKeyboard()
  {   String line = "";
      try
      {  line = reader.readLine();
      } catch (IOException e)
      {  e.printStackTrace();
      }
      return line;
  }
}
Listing 4.3.9. Definition of the KeyboardReader class.

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 (java.io.BufferedReader).

Next notice how we create a BufferedReader object in the KeyboardReader() constructor:

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

The resulting 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 readKeyboard(). The 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 prompt() and 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.

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 GreeterApp a 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.

Figure 4.3.11. Using KeyboardReader as the user interface.

The full implementation of the GreeterApp class is shown in ListingĀ 4.3.12. It begins by declaring an instance

public class GreeterApp
{   private KeyboardReader reader;
    public GreeterApp()
    {   reader = new KeyboardReader();
    } // GreeterApp()
    public void run()
    {   String name = "";
        reader.prompt("Please input your name here > ");
        name = reader.getKeyboardInput();
        reader.display(greet(name) + "\n");
    } // run()
    public String greet(String name)
    {   return "Hi " + name + " nice to meet you.";
    } // greet()
    public static void main(String args[])
    {   GreeterApp app = new GreeterApp();
        app.run();
    }
  } // GreaterApp
Listing 4.3.12. Definition of the GreeterApp class.

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 run() method.

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.

Java's 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);
Given this statement, design and implement an application that will play the following guessing game with the user. The computer generates a random number between 1 and 100 and then lets the user guess the number, telling the user when the guess is too high or too low. Note that for this problem, the user will have to input integers at the keyboard, so you will have to use a development environment with user input possible, like replit. Paste your code or the link to your code here to turn in.

You have attempted of activities on this page.