Introduction

In this chapter we will look at how to make our programs interactive by allowing input from the keyboard. For this we will use the Scanner. The Scanner is a pre-written class provided by the developers of Java. It isn’t really any different to our own classes in terms of format, except the Scanner class contains code for the purpose of reading/parsing input. There are, in fact, a few thousand classes in the Java Platform, which is one of the reasons they are organised into packages. The Scanner is located in a package called java.util. Therefore the fully qualified name of it is java.util.Scanner. This is important because, in order to use the Scanner, you first need to import it into your program. To do so, at the very top of the file, write

import java.util.Scanner;

The program should look something like this:

import java.util.Scanner;

public class UserInput1 {
    public static void main(String[] args) {

    }
}

The import statement on line 1 imports the definition of the Scanner into this file, which enables our program to recognise what a Scanner is and what it can do. However, the Scanner works differently to the classes we’ve seen so far. Our own classes, such as Main and HelloWorld, just work out of the box. When we hit Run, the main method executes straight away. This is also the case with the class Math where we used one of its methods in the previous chapter, the sqrt method. The methods inside these classes can be called and executed directly and without any sort of preparation.

In contrast, the Scanner is one of those classes that has been specifically designed to work more like a template, or blueprint. We must create a Scanner object from the Scanner class in order to use it. This is analogous to the real world, where things like templates and other guides are used to create tangible, usable objects such as food, materials, vehicles, buildings, bridges, electronic devices, and more. A class can be thought of as a template/blueprint from which usable, functioning objects can be created. Specifically, the Scanner class is a template for creating usable, functioning Scanners. These Scanners reside in memory and it is these that perform work in our programs. We can create as many Scanners as we want to perform different scanning tasks, but we’re only going to need one to read from the keyboard.

Objects are also known as instances since each object is one instance of its class. When we create an instance of a class, it is called instantiation. This is analogous to using a recipe to bake 100 cakes. Each cake is an instance of the recipe. It’s the cakes that do the real work i.e. they fill you up, whereas the recipe’s purpose is simply to instantiate more cakes. Likewise, the Scanner class’s purpose is to instantiate Scanner(s), which do the real work (of scanning data). Let’s see this in action. To instantiate a Scanner, type the following line in main:

Scanner x = new Scanner(System.in);

Like in the previous chapter, this line is assigning a value to a variable, as indicated by the assignment operator (=). The left side (Scanner x) is a simple variable declaration. The variable is called x and its type is Scanner, meaning x can hold a Scanner object. The right side (new Scanner(System.in)) is the instantiation (creation) of a Scanner. The new keyword creates objects, ergo new Scanner creates a Scanner object. Between the parentheses we specify the source this Scanner shall read from. It reads System.in. System is a class and in is a (member) variable inside System. This variable is predefined and contains a “reference” to the keyboard.

In short, the full line of code creates a Scanner that will read input from the keyboard and is assigned to the variable x. It might sound strange that a Scanner can be stored in a variable but, remember, it’s just bits and bytes on a fundamental level. What these bits and bytes represent, we call a Scanner, or more generally, an object.

Note
In Java, strings are objects too. String is a class just like Scanner is a class. Whenever we write text between double-quotes, behind the scenes a String object is instantiated in memory containing a representation of the string.

Our program should now look like this:

import java.util.Scanner;

public class UserInput1 {
    public static void main(String[] args) {
        Scanner x = new Scanner(System.in);
    }
}

The variable x is essentially our gateway to the Scanner. On the next line, type x. (x-dot) and you’ll be able to see all the methods inside the Scanner. Now call the next method underneath by typing x.next();

Scanner x = new Scanner(System.in);
x.next();

This method doesn’t need any data passed to it, hence why its parentheses are left empty. Let’s run the program. You’ll notice that the program doesn’t end straight away like before. The line x.next(); is causing the program to halt because the next method is waiting for some input. In the Run Window, type some text like Hello and then press enter. The next method will process this text and then the program continues. At the moment, we’re not doing anything with the text so the program just ends.

You know the program has ended when you see the line “Process finished with exit code 0”. The next method returns the text as a String. To obtain it, we can store it in a variable like so:

Scanner x = new Scanner(System.in);
String y = x.next();

Afterwards, we’ll print said variable:

Scanner x = new Scanner(System.in);
String y = x.next();
System.out.print(y);

To clarify, line 2 calls the Scanner’s method next with x.next(). This method blocks the program and waits for input. Once some text is entered, the method returns the text, which goes into variable y. Line 3 then prints y. In essence, whatever we enter gets printed back out. If we run the program, type in some text, and press enter, we’ll see the text gets printed back out underneath.

Note that next only returns one word. If you try inputting a sentence with spaces, next will only return the first word in the sentence. This is because your input is a sequence of characters that next will form into a string and it stops once it finds a whitespace character. A whitespace character is a character that has a presence in the string but isn’t visible, such as a space or a newline.

There’s one more thing we should do before moving on, and that is to give our variables better names. Our Scanner variable is called x. That’s not a very descriptive name because x could mean anything. If we saw x deep within the code, we would have to spend time working out its type and purpose. Since the variable contains a Scanner, why not call the variable itself “scanner” (with a lowercase s):

Scanner scanner = new Scanner(System.in);

It’s quite common to see a variable be given the same name as its type, but with a lowercase letter. However, the name you give to a variable is ultimately up to you. If you want a shorter name to save typing, something like scan or sc would also be suitable:

Scanner scan = new Scanner(System.in);
Scanner sc = new Scanner(System.in);

Different kinds of names like keyboardReader or simply keyboard are also plausible, since these describes the Scanner’s purpose:

Scanner keyboardReader = new Scanner(System.in);
Scanner keyboard = new Scanner(System.in);

Let’s go with sc, since it’s short and unambiguous at this stage. Don’t forget to change all the places the variable is used from x to sc. Again, you can either do this manually or right-click the variable and go Refactor > Rename. The other variable, y, is also a poor name. It should be called something descriptive like string, str, text, or input. Let’s go with input. With these changes, our program now looks like this:

import java.util.Scanner;

public class UserInput1 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String input = sc.next();
        System.out.print(input);
    }
}

The behaviour of our program remains exactly the same as it was before but at least now the variables have identity, making them easier to understand and use throughout the program.

An Interactive Program

The following program asks the user a question and responds when the user answers.

System.out.println("What is your name?");
Scanner sc = new Scanner(System.in);
String input = sc.next();
System.out.print(input + " is a nice name.");
[Run]
What is your name?
Sarah
Sarah is a nice name.

First, line 1 simply prints out the text “What is your name?” Line 2 creates a Scanner and stores it in a variable called sc.

Line 3 calls sc.next(). The next method blocks for input. From the output we can see the user enters “Sarah”. Therefore, the method returns “Sarah”, which is subsequently placed in variable input. Line 4 joins input with the text “ is a nice name.” resulting in “Sophie is a nice name.” This string is then printed.

The Scanner has a number of methods like next such as nextByte, nextInt, nextDouble, nextBoolean, etc. The difference between them is the data type they return. As we’ve seen, next returns a String, and the rest are self-descriptive: nextByte returns a byte, nextInt returns an int, nextDouble returns a double, etc. However, if nextInt is called and a letter is entered, the program will crash since a letter cannot be interpreted as an integer. We’ll see how to solve this kind of problem later on.

Let’s now ask a second question, “How old are you?”

System.out.println("What is your name?");
Scanner sc = new Scanner(System.in);
String input = sc.next();
System.out.print(input + " is a nice name.");

System.out.println("How old are you?");
input = sc.next();
System.out.printf("You are %s years old.\n", input);
[Run]
What is your name?
Nick
Nick is a nice name.
How old are you?
27
You are 27 years old.

Lines 1-4 are the same as before. Line 6 prints “How old are you?” Line 7 calls sc.next(), which returns the user’s input into variable input. This is the same variable declared on line 3. We are reusing it for the next question, thus overwriting its content. Line 9 prints out a response.

Notice on line 9 I’m using printf this time instead of print or println. This is more a personal choice and any of these print methods would do the job. I prefer printf as it’s easier to read, especially when variables are involved because there is no need for concatenation like on line 4. This is one of the many reasons programming is considered as much an art as a science.

There are some more changes we can make to this program to make it more readable:

Scanner sc = new Scanner(System.in);

System.out.println("What is your name?");
String name = sc.next();
System.out.println(name + " is a nice name.");

System.out.println("How old are you?");
String age = sc.next();
System.out.printf("You are %s years old.\n", age);

First, the creation of the Scanner has been moved to the top of main (line 1). Second, the names of the variables have been changed as the name input was becoming too generic. On line 4, the variable has been changed to name since that is more specific. For the same reason, on line 9, a new variable age has been declared for the user’s age. To the user, the program is exactly the same, but from a programmer’s perspective, there’s a slight improvement in the structure and consistency of the code. That is, set up the Scanner first to get it out of the way, then underneath comes the two questions with descriptive variables. For such a simple program, this barely makes a difference, but as a program gets larger, the structure of the code becomes increasingly more important.

Another possible improvement is to retrieve the age as an int and not a String:

System.out.println("How old are you?");
int age = sc.nextInt();
System.out.printf("You are %d years old.\n", age);

On line 2, instead of calling sc.next for a String, we call sc.nextInt for an int. The variable age has also been changed to an int to accommodate the value. On line 3, since age is an int, we need to use %d for (decimal) integers.

The reason for this change is because we can do calculations easily with an int, but not with a String. For example, we can tell the user how old they would be in twice their current age.

System.out.println("How old are you?");
int age = sc.nextInt();
System.out.printf("You are %d years old.\n", age);
System.out.printf("In another %d years you'll be %d years old.\n", age, 2 * age);

[Run]

How old are you?
27
You are 27 years old.
In another 27 years you’ll be 54 years old.

On line 4, in the first argument (the string), you can see there are two %d format specifiers. These will be replaced with the two arguments that follow: age and 2 * age. Therefore, the output is basically this: “In another age years you’ll be 2 * age years old.” This results in: “In another 27 years you’ll be 54 years old.”

Let’s now prompt the user for their height. It doesn’t have to be in the form of a question either. Add the following code at the bottom of main and run the program.

System.out.print("Enter your height (metres): ");
double height = sc.nextDouble();
System.out.printf("You are %.2fm tall.", height);
[Run]
...
Enter your height (metres): 2.64
You are 2.64m tall.

Line 1 asks the user to enter their height. On line 2, sc.nextDouble will obtain the input and return it into variable height (as a double). When running the program, you’ll notice that we are able to type on the same line as the prompt. This is because line 1 uses print instead of println, which means no newline is placed at the end, therefore our input continues on the same line. Finally, line 3 prints “You are heightm tall.” rounded to 2 decimal places.

Comment-out Code

When we run the program to test a newly added piece of code, we have to go through the whole program. This is tiresome. We just want to enter the height to see if that works but we have to enter the name and age before we get to that, every time! To isolate a particular piece of code we can simply comment out the rest. For example, a multi-line comment uses the characters /* and */ like this:

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

    /*System.out.println("What is your name?");
    String name = sc.next();
    System.out.println(name + " is a nice name.");

    System.out.println("How old are you?");
    int age = sc.nextInt();
    System.out.printf("You are %d years old.\n", age);
    System.out.printf("In another %d years you'll be %d years old.\n", age, 2 * age);*/

    System.out.print("Enter your height (metres): ");
    double height = sc.nextDouble();
    System.out.printf("You are %.2fm tall.", height);
}

Java will ignore everything between the characters /* and */ at the start of line 4 and end of line 11. We can then run the program as if that code doesn’t exist. Alternatively, you can highlight the chunk of code and press Ctrl+/ (control+forward-slash). This will automatically comment (or uncomment) each line of code using single-line comments (//):

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

//    System.out.println("What is your name?");
//    String name = sc.next();
//    System.out.println(name + " is a nice name.");
//
//    System.out.println("How old are you?");
//    int age = sc.nextInt();
//    System.out.printf("You are %d years old.\n", age);
//    System.out.printf("In another %d years you'll be %d years old.\n", age, 2 * age);

    System.out.print("Enter your height (metres): ");
    double height = sc.nextDouble();
    System.out.printf("You are %.2fm tall.", height);
}

Comments are a quick and dirty technique to save time and/or find bugs by skipping potentially problematic lines of code.

Reading Lines

The Scanner methods we’ve seen so far only scan single words. Specifically, they only scan tokens, which are sequences of characters delimited by whitespace. For example, the Scanner would tokenise the sentence “What is the weather like today?” into six tokens i.e. “What”, “is”, “the”, “weather”, “like”, and “today?” What if we want the whole sentence though? Take this program, for example:

import java.util.Scanner;

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

        System.out.printf("Enter name: ");
        String name = sc.next();
        System.out.printf("Your name is %s.", name);
    }
}

Nothing new here. This program simply asks for the user’s name and prints it back out. But what happens if we enter a name like “John Doe Dory”:

[Run]
Enter name: John Doe Dory
Your name is John.

As we can see, sc.next only takes and returns the first token, “John”. Therefore, we only get “Your name is John.” The tokens “Doe” and “Dory” aren’t discarded, however. They are still waiting to be read. So we could call sc.next three times in a row and combine them with the concatenation operator (+). The disadvantage of that is we have to enter three words every time, no more, no less. Also, we would have to manually insert spaces since sc.next discards them. No, a better solution is to use the nextLine method.

import java.util.Scanner;

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

        System.out.printf("Enter name: ");
        String name = sc.nextLine();
        System.out.printf("Your name is %s.", name);
    }
}
[Run]
Enter name: John Doe Dory
Your name is John Doe Dory.

On line 8, sc.nextLine() is called. The nextLine method takes the whole line of input and returns it. It is placed into name, which is then printed on line 9.

An Input Problem

There is an unfortunate problem we run into when combining the various Scanner methods as demonstrated in the following program.

import java.util.Scanner;

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

        System.out.print("Enter passcode: ");
        int passcode = sc.nextInt();
        System.out.printf("%d Accepted.\n", passcode);

        System.out.printf("Enter name: ");
        String name = sc.nextLine();
        System.out.printf("Your name is %s.", name);
    }
}

At first glance, this program looks like it will work fine. Line 7 asks for a passcode. Line 8 obtains the passcode using nextInt and returns it into variable passcode. Line 9 prints that the passcode is accepted (it doesn’t matter what number is entered). Lines 11 asks for a name. Line 12 obtains the name. Line 13 prints the name. However, the output tells a different story.

[Run]
Enter passcode: 1234
1234 Accepted.
Enter name: Your name is .

The entering of the passcode looks like it worked fine, but something went wrong with the name. As soon as we entered a passcode, the program zoomed through the remaining lines of code without waiting for input on line 12 like we’d expect. Here is what happened:

  1. On line 8, when we enter a number for the passcode, like 1234, we have to press enter. Pressing enter produces a newline (\n). So really, we’re entering 1234\n. As a consequence, nextInt reads the passcode but stops once it reaches the \n, which means the \n is still waiting to be read. The Scanner itself keeps track of where it has read up to so any further calls to its methods will read from this point onwards.
  2. Line 9 prints “1234 Accepted.”
  3. Line 11 prints “Enter name: “
  4. On line 12, sc.nextLine() does not wait for input because it sees there is a \n waiting and immediately reads it. However, this particular method discards newlines so it will simply return an empty string. Therefore, variable name will contain an empty string (“”).
  5. On line 13, since name contains an empty string, the %s gets replaced with nothing, so it prints “Your name is .”

One solution to this problem is to put in an extra call to sc.nextLine() after sc.nextInt():

import java.util.Scanner;

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

        System.out.print("Enter passcode: ");
        int passcode = sc.nextInt();
        sc.nextLine();
        System.out.printf("%d Accepted.\n", passcode);

        System.out.printf("Enter name: ");
        String name = sc.nextLine();
        System.out.printf("Your name is %s.", name);
    }
}

The call to nextLine on line 9 ensures that the \n is read, which means there is nothing left to read for future calls. Therefore, when nextLine is called again on line 13, it will block and wait for the user to enter something.

[Run]
Enter passcode: 1234
1234 Accepted.
Enter name: John Doe Dory
Your name is John Doe Dory.

In summary, nextLine is the odd one out when it comes to the various nextX methods. The key difference between them is that nextLine returns the entire line but discards any \n at the end, whereas the other nextX methods skip over newlines and other whitespace characters entirely until they find a valid token they can return. This can be distilled to a simple rule:

  • Whenever you call next, nextInt, nextDouble, etc, and then you want to call nextLine afterwards, you should put an additional call to nextLine somewhere in between. This ensures the \n is consumed and the program will then behave as expected.

Leave a Reply

Your email address will not be published. Required fields are marked *