Scanner Patterns
Input validation, the buffer trap, and robust user interaction
After this lesson, you will be able to:
- Use
nextInt(),nextDouble(),nextLine(), andnext()correctly - Explain and fix the
nextInt()/nextLine()buffer trap - Validate input with
hasNextInt()andhasNextDouble()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 bynextLine(). 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.
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
hasNextmethods 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 usewhile (scanner.hasNext())to read until the file ends.
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.
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.