java-foundations Lesson 8 16 min read

Scanner Patterns

Input validation, the buffer trap, and robust user interaction

Reading: Reges & Stepp: Ch. 3 §3.3

After this lesson, you will be able to:

  • Use nextInt(), nextDouble(), nextLine(), and next() correctly
  • Explain and fix the nextInt()/nextLine() buffer trap
  • Validate input with hasNextInt() and hasNextDouble() before reading
  • Combine Scanner with variable scope to build robust input programs

The Mystery of the Disappearing Input

A student writes a program to read a number and then a name:

Scanner scanner = new Scanner(System.in);

System.out.print("Enter age: ");
int age = scanner.nextInt();

System.out.print("Enter name: ");
String name = scanner.nextLine();

System.out.println("Hello, " + name + "! You are " + age);

They type 21 and press Enter. The program immediately prints Hello, ! You are 21 — it skipped the name prompt entirely. The student sees a ghost. What happened?

This is the Scanner buffer trap, and it catches nearly every Java beginner.

From CSCD 110: Python’s input() always reads a complete line and returns a string. No buffer, no trap. Java splits input reading into token methods (nextInt, nextDouble, next) and line methods (nextLine), and mixing them causes this exact problem.


How the Scanner Buffer Works

When the user types 21 and presses Enter, the input buffer contains:

21\n

nextInt() reads the 21 but leaves the \n in the buffer. When nextLine() runs next, it sees the leftover \n and immediately returns an empty string — it thinks the user pressed Enter on an empty line.

Before nextInt():   [2][1][\n]
After nextInt():    [\n]           ← leftover!
After nextLine():   []             ← consumed the \n, returned ""

The fix is simple — consume the leftover newline with a throwaway nextLine():

System.out.print("Enter age: ");
int age = scanner.nextInt();
scanner.nextLine();  // consume the leftover \n

System.out.print("Enter name: ");
String name = scanner.nextLine();  // now reads the actual name

Key Insight: The buffer trap happens whenever a token-reading method (nextInt, nextDouble, next) is followed by nextLine(). Token methods skip whitespace before the value but leave the newline after it. nextLine() reads everything up to the next newline — and if there is a leftover newline, it returns an empty string immediately.

Check Your Understanding

What causes the Scanner buffer trap?

Scanner sc = new Scanner(System.in);
// Input: 21↵Alice↵
int age = sc.nextInt();
String name = sc.nextLine();
System.out.println("Age: " + age);
System.out.println("Name: " + name);

The Scanner Method Toolkit

Scanner has two families of reading methods:

Token-reading methods — skip leading whitespace, read one value, leave \n:

Method Reads Leaves \n?
nextInt() One integer Yes
nextDouble() One double Yes
next() One word (to next whitespace) Yes

Line-reading method — reads the entire line including the \n:

Method Reads Leaves \n?
nextLine() Everything to the next \n No (consumes it)

The buffer trap occurs when you switch from a token method to nextLine(). It does not happen when:

  • You use only nextLine() calls
  • You use only nextInt() / nextDouble() calls in sequence
  • You put nextLine() before any token method

Input Validation with hasNext

What happens if you call nextInt() and the user types “hello”? Your program crashes with an InputMismatchException. The hasNext methods let you check before reading:

Method Returns true if…
hasNext() Any token is available
hasNextInt() The next token is a valid int
hasNextDouble() The next token is a valid double
hasNextLine() Another line of input exists
Scanner scanner = new Scanner(System.in);

System.out.print("Enter your age: ");
if (scanner.hasNextInt()) {
    int age = scanner.nextInt();
    System.out.println("Your age is " + age);
} else {
    System.out.println("That is not a valid integer!");
    scanner.next();  // consume the bad input
}

Key Insight: The hasNext methods peek at the buffer without consuming anything. They are the “look before you leap” pattern — check that the input is valid before reading it. This becomes essential for file processing in Week 7, where you use while (scanner.hasNext()) to read until the file ends.

Check Your Understanding

What does hasNextInt() do if the next token in the buffer is "3.14"?


next() vs nextLine()

These are easy to confuse:

// User types: "Alice Wonderland"

String word = scanner.next();       // "Alice"  (one word)
String rest = scanner.nextLine();   // " Wonderland" (rest of line)

// vs.
String full = scanner.nextLine();   // "Alice Wonderland" (entire line)

next() reads one whitespace-delimited token. nextLine() reads the entire line. Use next() when you want a single word; use nextLine() when you want an entire line including spaces.

The buffer trap applies to next() too — it is a token method that leaves \n in the buffer.


Variable Scope and Scanner

When reading input with conditional validation, you need to think about where you declare variables. This is a common mistake:

if (scanner.hasNextInt()) {
    int age = scanner.nextInt();    // declared inside if-block
}
System.out.println(age);  // COMPILER ERROR: age is out of scope!

The fix: declare the variable before the block:

int age;  // declared in the outer scope
if (scanner.hasNextInt()) {
    age = scanner.nextInt();       // assigned inside if-block
} else {
    age = -1;                      // default value
}
System.out.println(age);  // OK: age is in scope

The Trick: If you need a variable after a block, declare it before the block. Assign inside, declare outside.


Complete Example: Student Record Entry

Here is a complete program combining the buffer trap fix, input validation, and scope:

import java.util.Scanner;

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

        System.out.print("Enter student name: ");
        String name = scanner.nextLine();

        System.out.print("Enter first score: ");
        if (!scanner.hasNextInt()) {
            System.out.println("Error: not a valid integer.");
            return;
        }
        int score1 = scanner.nextInt();

        System.out.print("Enter second score: ");
        if (!scanner.hasNextInt()) {
            System.out.println("Error: not a valid integer.");
            return;
        }
        int score2 = scanner.nextInt();

        double average = (score1 + score2) / 2.0;

        System.out.println("Student: " + name);
        System.out.println("Scores:  " + score1 + ", " + score2);
        System.out.printf("Average: %.1f%n", average);
    }
}

Sample run:

Enter student name: Alice Johnson
Enter first score: 92
Enter second score: 87
Student: Alice Johnson
Scores:  92, 87
Average: 89.5

Notice that nextLine() is called first (for the name), before any nextInt(). This avoids the buffer trap entirely. If you reversed the order — reading scores first, then the name — you would need the throwaway scanner.nextLine() fix.

Check Your Understanding

Which reading order avoids the buffer trap without any fix?


Quick Reference: Avoiding Scanner Bugs

Situation Safe? Fix
nextInt()nextInt() Yes Token methods skip whitespace
nextLine()nextLine() Yes Each consumes its own \n
nextLine()nextInt() Yes nextInt() skips whitespace
nextInt()nextLine() No Add throwaway scanner.nextLine()
nextDouble()nextLine() No Add throwaway scanner.nextLine()
next()nextLine() No Add throwaway scanner.nextLine()

Summary

The Scanner buffer holds everything the user types. Token-reading methods (nextInt, nextDouble, next) consume a value but leave the trailing newline. nextLine() consumes everything through the newline. Mixing token methods with nextLine() causes the buffer trap — fix it with a throwaway scanner.nextLine() call.

The hasNextInt() and hasNextDouble() methods let you validate input before reading, preventing InputMismatchException crashes. They peek at the buffer without consuming anything.

Variable scope matters when combining conditionals with Scanner: declare variables before the block if you need them after the block.

Next lesson: Complex conditionals — cumulative algorithms, character processing, and combining if/else with loops for more powerful decision-making.