Java User Input (Scanner)
Most real programs don’t just print output — they react to what the user types. In Java, the standard way to read input from the keyboard is the Scanner class from the java.util package. It wraps a raw input source (like System.in) and gives you convenient methods to pull out words, whole lines, integers, doubles, and more, converting the raw text into the Java types your program actually needs.
This lesson covers everything you need to use Scanner confidently: how it works internally, its full method set, several worked examples, the infamous nextInt()/nextLine() bug that trips up nearly every beginner, and the best practices professionals actually follow.
Overview / How it works
Scanner is a text-parsing utility. You construct it around a source of characters — most commonly the standard input stream System.in, which represents keyboard input in a console program — and then call methods on it to extract typed values one piece at a time.
Internally, a Scanner keeps an input buffer and a delimiter pattern (by default, whitespace: spaces, tabs, and newlines). When you ask it for the next token — say, with next() or nextInt() — it reads characters from the underlying stream into its buffer until it has enough text to identify a complete token, then it matches that token against the delimiter pattern to figure out where the token starts and ends. The matched token is handed back to you, parsed into the requested type if necessary.
Because System.in is a blocking stream, your program actually pauses at the point it calls a Scanner method until the user types something and presses Enter. This is why console programs appear to “wait” for input — the JVM thread executing main is blocked inside the read call until data arrives.
A crucial distinction to understand from the start: Scanner has two families of methods that behave differently.
- Token-based methods like
next(),nextInt(),nextDouble()read a single whitespace-delimited token and leave everything else — including the newline character that ended that line — sitting unread in the buffer. - Line-based methods, namely
nextLine(), read everything up to (and consume) the next line terminator, regardless of how many tokens are on that line.
Mixing these two families without care is the single most common source of bugs when learning Scanner, and it’s covered in detail in the Common Mistakes section below.
Scanner isn’t limited to console input — you can also construct one around a String, a File, or any InputStream, which makes it useful for parsing text files or in-memory data, not just keyboard input.
Syntax
The general pattern for reading console input is:
Scanner scanner = new Scanner(System.in);
int value = scanner.nextInt();
scanner.close();
- new Scanner(System.in) — creates a Scanner attached to the keyboard input stream. Requires
import java.util.Scanner;at the top of the file. - scanner.nextInt() / nextDouble() / nextLong() / nextBoolean() — reads and parses the next whitespace-delimited token as that primitive type. Throws
InputMismatchExceptionif the token doesn’t match, andNoSuchElementExceptionif there is no more input. - scanner.next() — reads the next whitespace-delimited token as a
String(stops at the first space). - scanner.nextLine() — reads all remaining characters on the current line as a
String, up to but not including the line terminator, and consumes that terminator. - scanner.hasNext() / hasNextInt() / hasNextDouble() / hasNextLine() — look ahead to check whether another token/line of the given type is available, without consuming it. Essential for input validation and loops.
- scanner.close() — releases the resources held by the scanner. Closing a Scanner wrapped around
System.inalso closesSystem.initself for the rest of the program.
| Method | Returns | Consumes trailing newline? |
|---|---|---|
| next() | String (one token) | No |
| nextInt() / nextDouble() | int / double | No |
| nextLine() | String (rest of the line) | Yes |
| hasNextInt() | boolean | No (lookahead only) |
Examples
Example 1: Reading a name and an age
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Enter your name: ");
String name = scanner.nextLine();
System.out.print("Enter your age: ");
int age = scanner.nextInt();
System.out.println("Hello, " + name + "! In 10 years you will be " + (age + 10) + ".");
scanner.close();
}
}
Output (assuming the user types Alice and then 30):
Enter your name: Alice
Enter your age: 30
Hello, Alice! In 10 years you will be 40.
Here nextLine() reads the whole first line as the name, and nextInt() then reads just the numeric token typed on the next line. Since this program doesn’t read anything after the nextInt() call, the newline-leftover issue described later doesn’t cause a problem here.
Example 2: Reading numeric values and computing a result
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Enter quantity: ");
int quantity = scanner.nextInt();
System.out.print("Enter unit price: ");
double unitPrice = scanner.nextDouble();
double total = quantity * unitPrice;
double totalWithTax = total * 1.08;
System.out.printf("Subtotal: $%.2f%n", total);
System.out.printf("Total with 8%% tax: $%.2f%n", totalWithTax);
scanner.close();
}
}
Output (assuming the user types 3 and then 19.99):
Enter quantity: 3
Enter unit price: 19.99
Subtotal: $59.97
Total with 8% tax: $64.77
nextInt() and nextDouble() each pull exactly one token and automatically convert it to the requested numeric type. Scanner even accepts input across separate lines here since whitespace — including newlines — is the delimiter.
Example 3: Looping until non-numeric input
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int sum = 0;
int count = 0;
System.out.println("Enter numbers one at a time. Type 'done' to finish.");
while (scanner.hasNextInt()) {
int number = scanner.nextInt();
sum += number;
count++;
}
System.out.println("You entered " + count + " numbers.");
System.out.println("Sum: " + sum);
scanner.close();
}
}
Output (assuming the user types 4, 7, 12, then done):
Enter numbers one at a time. Type 'done' to finish.
You entered 3 numbers.
Sum: 23
This example shows the real power of the hasNextXxx() family: hasNextInt() peeks at the next token without consuming it. As soon as the user types something that isn’t a valid integer (done), the loop condition becomes false and the loop exits cleanly, without throwing an exception.
How it works step by step / Under the hood
When you call a method like scanner.nextInt(), the following happens internally:
- The scanner checks its internal buffer for a token that isn’t yet consumed. If the buffer is empty or exhausted, it performs a blocking read from the underlying
InputStream(forSystem.in, this waits for keyboard input followed by Enter). - It applies the delimiter pattern (whitespace by default) to locate where the next token begins and ends.
- For
nextInt()specifically, the scanner first checks — via an internal call equivalent tohasNextInt()— whether the token matches an integer pattern for the current locale. If it matches, the token is consumed and parsed into anint. - If the token does not match (say, the user typed
abc), the scanner throws anInputMismatchExceptionand, importantly, does not consume the bad token — it’s still sitting in the buffer waiting to be read again. This is why callingnextInt()in a loop after a bad input without first consuming the offending token causes an infinite loop of exceptions. nextLine()works differently: it doesn’t look for a delimited token at all. It reads every character up to the next line terminator (\n,\r\n, or end of input), returns that as a String, and consumes the terminator itself so the next read starts on a fresh line.
This difference in what each method consumes is exactly what causes the classic beginner bug described next.
Common Mistakes
Mistake 1: Mixing nextInt()/nextDouble() with nextLine()
When you call nextInt(), it consumes only the numeric token — the newline character produced when the user pressed Enter is left behind in the buffer. If you then call nextLine(), it immediately matches that leftover newline and returns an empty string instead of waiting for new input.
Wrong:
java.util.Scanner scanner = new java.util.Scanner(System.in);
System.out.print("Enter your age: ");
int age = scanner.nextInt();
System.out.print("Enter your favorite color: ");
String color = scanner.nextLine();
System.out.println("Age: " + age + ", Color: " + color);
Output (user types 25, then blue):
Enter your age: 25
Enter your favorite color: Age: 25, Color:
Notice color ends up empty — the second prompt never actually gets a chance to be answered, because the leftover newline from the age input satisfied the nextLine() call instantly.
Corrected (consume the leftover newline first):
java.util.Scanner scanner = new java.util.Scanner(System.in);
System.out.print("Enter your age: ");
int age = scanner.nextInt();
scanner.nextLine();
System.out.print("Enter your favorite color: ");
String color = scanner.nextLine();
System.out.println("Age: " + age + ", Color: " + color);
Output (user types 25, then blue):
Enter your age: 25
Enter your favorite color: blue
Age: 25, Color: blue
The extra, unassigned scanner.nextLine() call absorbs the leftover newline so the following nextLine() genuinely waits for new user input.
Mistake 2: Not validating input before parsing
Calling nextInt() when the user might type a non-numeric value crashes the program with an uncaught InputMismatchException. Always guard risky reads with hasNextInt() (or wrap the parse in a try/catch) instead of assuming the input will always be well-formed.
Mistake 3: Closing the Scanner too early
Calling scanner.close() on a Scanner wrapped around System.in closes the underlying standard input stream for the rest of the JVM process. If any code afterward — even a brand new Scanner object — tries to read from System.in again, it will fail. Only close your System.in-based Scanner once, right before the program exits, and never open a second one over the same stream after closing the first.
Best Practices
- Create exactly one
ScannerforSystem.inand reuse it throughout the program instead of creating multiple instances. - Use
hasNextInt(),hasNextDouble(), orhasNext()to validate input before consuming it, especially in loops that read repeated values. - After a numeric read (
nextInt(),nextDouble(), etc.), call an extranextLine()if you plan to read a full line next, to clear the leftover newline. - Prefer reading everything with
nextLine()and parsing manually (e.g.Integer.parseInt(line.trim())) when you need clear, custom error messages for malformed input. - Only call
close()once, at the very end of the program’s execution. - Give the user a clear prompt with
System.out.print(no newline) immediately before each read, so it’s obvious what’s expected. - For very large volumes of text input where performance matters, consider
BufferedReaderinstead —Scanner‘s regex-based tokenizing has more overhead.
Practice Exercises
- Exercise 1: Write a program that reads three integers typed on a single line, separated by spaces, and prints their average as a decimal.
- Exercise 2: Write a program that repeatedly prompts the user to enter a number and adds it to a running total, stopping as soon as the user types a non-numeric value, then prints the count and total (use
hasNextInt()as shown in Example 3, but print a fresh prompt before each read). - Exercise 3: Write a program that reads a person’s full name on one line with
nextLine(), then reads their age withnextInt(), then reads their city with anothernextLine()— make sure the city isn’t accidentally read as an empty string.
Summary
Scanner, fromjava.util, reads and parses text from a source such asSystem.in, a file, or a String.- Token-based methods (
next(),nextInt(),nextDouble(), …) read one whitespace-delimited token and leave the trailing newline unread. nextLine()reads the rest of the current line and consumes the line terminator, which is why mixing it with token-based methods causes the classic “empty input” bug.hasNextXxx()methods let you safely check what’s coming next without consuming it, which is the key to writing crash-proof input loops.InputMismatchExceptionis thrown (without consuming the bad token) when a numeric read doesn’t match the actual input.- Close your
System.inScanner exactly once, at the end of the program, since closing it shuts down standard input for the whole process.
