Java Scanner — The Silent Skip That Lost Payment Data
- Scanner is a wrapper around any readable source — swap System.in for a File object and the entire API works identically, which means one skill covers two common use cases.
- nextInt(), nextDouble() and friends leave the newline character in the buffer — always consume it with an extra nextLine() before reading your next string to avoid the empty-string bug.
- Always use hasNextInt() (or hasNextDouble()) to validate input before parsing it — nextInt() on bad input throws InputMismatchException and crashes; hasNextInt() peeks safely without consuming the token.
- Scanner wraps System.in or File to read text input, converting tokens to Java types.
- Key methods: nextLine(), nextInt(), nextDouble() — each parses a specific type.
- hasNextInt() and similar let you validate before reading — prevents InputMismatchException crashes.
- Performance: Scanner is slower than BufferedReader for large files due to regex parsing overhead.
- Production trap: nextLine() after nextInt() silently reads leftover newline — always add an extra nextLine().
- Biggest mistake: Multiple Scanner objects on System.in — closing one closes the stream for all.
Quick Debug Cheat Sheet for Scanner Input
nextLine() skipping input
Add: inputReader.nextLine(); // discard leftover newlineIf you can change design: use Integer.parseInt(inputReader.nextLine()) for all number reading.InputMismatchException crash
if (scanner.hasNextInt()) { int num = scanner.nextInt(); } else { String bad = scanner.next(); // consume }Use scanner.next() to consume invalid token and avoid infinite loop.Scanner won't close or resource warning
For file: try (Scanner fileScanner = new Scanner(new File(...))) { ... }For System.in: create one global Scanner, close in finally after all input done.Infinite loop in validation
scanner.next(); // MUST consume bad inputCheck that loop condition will eventually become false when valid input arrives.Production Incident
next(), etc. is a silent state — invisible to most code reviews.Always insert a dummy nextLine() after any nextXxx() call that reads a token, if a nextLine() follows.Better yet: read everything as strings with nextLine() and parse numbers separately using Integer.parseInt() — this eliminates the newline problem entirely and makes parsing errors easier to handle.Production Debug GuideQuick symptom-to-action reference for common Scanner problems in production or development.
Integer.parseInt().scanner.next() to consume the bad token and prompt again.scanner.next() if input is invalid to remove it from the buffer.Every useful program in the real world needs to talk to a human. A login screen needs a username. A calculator needs two numbers. A quiz app needs an answer. Without the ability to read what a user types, your Java program is essentially a locked room — it can think, but it can't listen. That's where the Scanner class steps in, and it's one of the very first things every Java developer learns for exactly that reason.
Before Scanner existed, reading input from a keyboard in Java was genuinely painful. You had to work with low-level streams, wrap them in readers, and handle checked exceptions just to read a single number. Scanner wraps all of that complexity in a clean, beginner-friendly API. You get methods like nextLine(), nextInt(), and nextDouble() that read exactly the type of data you want — no ceremony required.
By the end of this article you'll know how to set up a Scanner, read strings and numbers from the keyboard, read input from a file, close the Scanner correctly, and — critically — avoid the two nasty bugs that trip up almost every beginner. You'll also have three fully runnable programs you can copy, run, and tweak right now.
What Is Scanner and How Do You Set It Up?
Scanner lives in the java.util package, which means you need to import it before you can use it. Think of an import like telling your kitchen which cookbook to pull off the shelf — you're telling Java 'I need the Scanner recipe from the java.util cookbook'.
Once imported, you create a Scanner object. An object is just a working instance of a class — like printing a specific form from a template. You hand the Scanner constructor a source to read from. The most common source is System.in, which represents the keyboard. Literally: System.in means 'standard input', which is the stream of characters that flows in when a user types.
The Scanner then sits and waits. When your code calls a method like nextLine(), the program pauses, a cursor blinks in the terminal, the user types something and hits Enter, and Scanner captures everything typed before that Enter key.
One important habit to build immediately: always close your Scanner when you're done with it. A Scanner holds a connection to its source (keyboard or file). Leaving it open is like leaving a tap running — it wastes resources and can cause subtle bugs in larger programs.
import java.util.Scanner; // Step 1: import Scanner from the java.util package public class GreetUser { public static void main(String[] args) { // Step 2: create a Scanner object connected to the keyboard (System.in) Scanner keyboardReader = new Scanner(System.in); // Step 3: prompt the user so they know what to type System.out.print("Enter your name: "); // Step 4: read the entire line the user types (including spaces) String userName = keyboardReader.nextLine(); // Step 5: use the input — here we just greet the user System.out.println("Hello, " + userName + "! Welcome to TheCodeForge."); // Step 6: always close the Scanner when you're finished reading keyboardReader.close(); } }
Hello, Sarah! Welcome to TheCodeForge.
System.out.print().Reading Different Data Types — Strings, Integers and Decimals
Scanner doesn't just read raw text. It can parse that text directly into the Java data type you need — int, double, boolean, and more. Each data type has its own dedicated method, and choosing the right one saves you from having to convert strings manually.
nextLine() reads an entire line of text up to (but not including) the newline character created by pressing Enter. It returns a String.
next() reads a single 'token' — one word, stopping at whitespace. Useful for reading one word at a time.
nextInt() reads the next token and tries to parse it as a whole number (int). If the user types '42', you get the integer 42, ready for arithmetic.
nextDouble() does the same for decimal numbers. The user types '3.14' and you get a double you can multiply, divide, or compare.
The program below builds a simple age-and-height form. Notice how we mix data types in one conversation — that's exactly what real programs do.
import java.util.Scanner; public class UserProfileForm { public static void main(String[] args) { Scanner inputReader = new Scanner(System.in); // --- Read a String --- System.out.print("What is your full name? "); String fullName = inputReader.nextLine(); // reads the whole line, spaces included // --- Read an integer --- System.out.print("How old are you? "); int age = inputReader.nextInt(); // parses the typed text directly into an int // --- Read a double --- System.out.print("What is your height in metres? "); double heightInMetres = inputReader.nextDouble(); // parses into a decimal number // --- Display a summary --- System.out.println("\n--- Profile Summary ---"); System.out.println("Name : " + fullName); System.out.println("Age : " + age + " years"); System.out.printf("Height : %.2f m%n", heightInMetres); // %.2f = 2 decimal places // Calculate something real with the input if (age >= 18) { System.out.println("Status : Adult"); } else { System.out.println("Status : Minor"); } inputReader.close(); } }
How old are you? 24
What is your height in metres? 1.68
--- Profile Summary ---
Name : Maria Gonzalez
Age : 24 years
Height : 1.68 m
Status : Adult
next() call if you plan to use nextLine() afterwards.next() reads one word; nextInt() reads an integer.Reading Input From a File — Same Tool, Different Source
Here's something that surprises many beginners: Scanner isn't just for keyboards. Because it accepts any source that Java can read from, you can point it at a text file and it reads line by line (or token by token) in exactly the same way. This is genuinely powerful — learn Scanner once, use it two ways.
To read from a file, you create a File object pointing at the file path, then pass that File object to the Scanner constructor instead of System.in. The rest of the API — nextLine(), nextInt(), hasNextLine() — works identically.
hasNextLine() is your best friend when reading files. It returns true as long as there are more lines to read, which makes it a perfect condition for a while loop. Without it you'd have to know in advance how many lines the file has.
Note that opening a file can fail (file not found, permission denied), so Java forces you to handle a FileNotFoundException. We use a try-with-resources block below, which is the modern, correct approach — it automatically closes the Scanner when the block ends, even if an exception occurs.
import java.util.Scanner; import java.io.File; // needed to represent the file on disk import java.io.FileNotFoundException; // thrown if the file doesn't exist public class FileLineReader { public static void main(String[] args) { // Imagine scores.txt contains: // Alice 95 // Bob 87 // Carol 92 File scoresFile = new File("scores.txt"); // point to the file // try-with-resources: Scanner is automatically closed when the block ends try (Scanner fileScanner = new Scanner(scoresFile)) { int lineNumber = 1; // hasNextLine() returns true if there is another line to read while (fileScanner.hasNextLine()) { String currentLine = fileScanner.nextLine(); // read one full line System.out.println("Line " + lineNumber + ": " + currentLine); lineNumber++; } System.out.println("\nFinished reading " + (lineNumber - 1) + " lines."); } catch (FileNotFoundException exception) { // This runs if scores.txt doesn't exist in the project directory System.out.println("Error: File not found — " + exception.getMessage()); } } }
Line 2: Bob 87
Line 3: Carol 92
Finished reading 3 lines.
close() in a finally block, and it's the pattern you'll see in all modern Java codebases.Validating Input in a Loop — Building a Bulletproof Input Reader
Real programs can't trust users to type perfectly. Someone will type 'abc' when you asked for a number. If nextInt() gets a non-numeric token it throws an InputMismatchException and crashes your program. That's a terrible user experience.
The professional solution is to validate input inside a loop. Scanner's hasNextInt() method (and its siblings hasNextDouble(), hasNextLong()) lets you check whether the next token is actually parseable as that type — before you try to parse it. Think of it as asking 'is the next thing in the queue actually a number?' before reaching in to grab it.
The loop below keeps asking until the user provides a valid positive integer. Notice two things: we call next() to consume the bad token when validation fails (otherwise Scanner is stuck on the same bad input forever), and we give the user a clear error message so they understand what went wrong.
import java.util.Scanner; public class SafeAgeInput { public static void main(String[] args) { Scanner console = new Scanner(System.in); int userAge = -1; // sentinel value: -1 means 'not yet set' // Keep looping until we have a valid age while (userAge < 0 || userAge > 130) { System.out.print("Please enter your age (0–130): "); if (console.hasNextInt()) { // check BEFORE parsing — safe! userAge = console.nextInt(); if (userAge < 0 || userAge > 130) { // The input was an integer but outside our acceptable range System.out.println("That age isn't realistic. Try again."); } } else { // The input wasn't an integer at all (e.g., the user typed 'hello') String badInput = console.next(); // MUST consume the bad token or we loop forever System.out.println("'" + badInput + "' is not a number. Please type digits only."); } } System.out.println("Great — you are " + userAge + " years old."); console.close(); } }
'hello' is not a number. Please type digits only.
Please enter your age (0–130): -5
That age isn't realistic. Try again.
Please enter your age (0–130): 27
Great — you are 27 years old.
next(), your loop becomes an infinite error printer — a classic bug that crashes nothing but infuriates users.next() in the else branch of a hasNextXxx() check to remove the invalid token from the buffer.next() in the else branch.Custom Delimiters — Reading Comma-Separated Data Like a Pro
By default, Scanner splits input on whitespace (spaces, tabs, newlines). But you can change this behavior using the useDelimiter() method. This is incredibly useful when you need to parse structured data like CSV lines, pipe-delimited logs, or any custom-separated input.
useDelimiter() takes a regular expression (or a Pattern) that defines the new delimiter. Once set, all nextXxx() methods will split tokens based on that pattern instead of whitespace.
A common use case: reading a line from a CSV file where values are separated by commas. You can set the delimiter to "," or a more robust pattern like ",|" to handle multiple delimiters.
Be careful: after setting a custom delimiter, nextLine() behavior changes — it reads tokens up to the newline, but the delimiter may include newline characters. Usually, for CSV parsing, you read each line with nextLine() first, then create a new Scanner for that line with a comma delimiter. That's the pattern shown below.
import java.util.Scanner; public class CsvParser { public static void main(String[] args) { String csvLine = "Alice,30,Engineer"; // Create a Scanner for the single line with comma delimiter Scanner lineScanner = new Scanner(csvLine); lineScanner.useDelimiter(","); // Read tokens in order String name = lineScanner.next(); // Alice int age = lineScanner.nextInt(); // 30 String job = lineScanner.next(); // Engineer System.out.println("Name: " + name); System.out.println("Age: " + age); System.out.println("Job: " + job); lineScanner.close(); } }
Age: 30
Job: Engineer
Scanner vs BufferedReader — When to Upgrade
Scanner is great for learning and small programs, but it has performance limitations. For reading large files (hundreds of MB), Scanner's regex-based tokenization becomes slow. That's when you reach for BufferedReader.
BufferedReader reads text from a character stream efficiently by buffering chunks of data. It only returns raw strings — you have to parse numbers or split lines yourself. But that trade-off gives you speed.
- You need to parse different data types from the same stream.
- Input size is small (a few MB or user keyboard input).
- You want simple methods like nextInt() without manual parsing.
- Performance matters (big files, high-frequency reads).
- You only need to read lines as strings.
- You want more control over encoding and buffer size.
If you're in a production system reading transaction logs, choose BufferedReader. If you're writing a quick CLI utility, Scanner is fine.
import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; public class BufferedReaderExample { public static void main(String[] args) { try (BufferedReader br = new BufferedReader(new FileReader("largefile.txt"))) { String line; while ((line = br.readLine()) != null) { // Manually parse if needed String[] parts = line.split(","); // process parts } } catch (IOException e) { System.err.println("Error reading file: " + e.getMessage()); } } }
Integer.parseInt() on the split tokens — still faster than Scanner.nextDouble().| Scanner Method | What It Reads | Returns | Stops At |
|---|---|---|---|
| nextLine() | Everything up to Enter key | String (can contain spaces) | Newline character \n |
| next() | One word / token | String (no spaces) | Any whitespace |
| nextInt() | Next token as a whole number | int | Whitespace (leaves \n behind) |
| nextDouble() | Next token as a decimal | double | Whitespace (leaves \n behind) |
| nextBoolean() | Next token as true/false | boolean | Whitespace |
| hasNextLine() | Does another line exist? | boolean (true/false) | Does not consume input |
| hasNextInt() | Is the next token an int? | boolean (true/false) | Does not consume input |
🎯 Key Takeaways
- Scanner is a wrapper around any readable source — swap System.in for a File object and the entire API works identically, which means one skill covers two common use cases.
- nextInt(), nextDouble() and friends leave the newline character in the buffer — always consume it with an extra nextLine() before reading your next string to avoid the empty-string bug.
- Always use hasNextInt() (or hasNextDouble()) to validate input before parsing it — nextInt() on bad input throws InputMismatchException and crashes; hasNextInt() peeks safely without consuming the token.
- Create exactly one Scanner per input source and close it exactly once — multiple Scanners on System.in will cause NoSuchElementException; use try-with-resources for file Scanners to guarantee cleanup even if an exception occurs.
- For large files or performance-critical reads, switch to BufferedReader — Scanner's regex-based tokenization causes significant overhead.
⚠ Common Mistakes to Avoid
Interview Questions on This Topic
- QWhat is the difference between
next()and nextLine() in Java's Scanner class, and when would you choose one over the other?JuniorReveal - QWhy does calling nextLine() immediately after nextInt() sometimes return an empty string, and how do you fix it?JuniorReveal
- QIf you need to read from both the keyboard and a file in the same program, how would you structure your Scanner usage to avoid resource leaks?Mid-levelReveal
- QHow does hasNextInt() differ from nextInt() in terms of behavior and safety?Mid-levelReveal
- QCan you use Scanner to read from a URL or network stream? How?SeniorReveal
Frequently Asked Questions
Do I need to close the Scanner after using it?
Yes, you should always close a Scanner when you're finished with it. An unclosed Scanner holds an open connection to its source (keyboard or file), which wastes system resources. For file Scanners, use try-with-resources to close automatically. For System.in, call close() at the end of your program — but be aware that closing it also closes System.in itself, so only do it once.
What is the difference between Scanner and BufferedReader in Java?
Scanner is easier to use for beginners because it parses data types directly (nextInt(), nextDouble(), etc.) and works with multiple sources including keyboards and files. BufferedReader is faster and better for reading large files line by line, but it only returns Strings — you have to parse types manually. For most beginner and intermediate programs, Scanner is the right choice. Switch to BufferedReader when performance with large files becomes a concern.
Why does my Scanner program skip a line of input without waiting for me to type?
This is almost certainly the nextInt()-then-nextLine() bug. When you read a number with nextInt(), it consumes the digits but leaves the Enter key's newline character in the input buffer. The immediately following nextLine() reads that leftover newline and returns instantly with an empty string. Fix it by adding an extra scanner.nextLine() call right after nextInt() to discard the leftover newline before you read your actual string.
Can Scanner read from sources other than keyboard and file?
Yes, Scanner can read from any object that implements Readable or provides an InputStream. You can pass a String directly to the constructor, or read from network sources by wrapping an InputStream from a URL or socket. This makes Scanner surprisingly versatile for simple parsing tasks.
What is the default delimiter for Scanner and how do I change it?
The default delimiter is any whitespace pattern defined by the regex \p{javaWhitespace}+. You can change it using scanner.useDelimiter(String pattern) or useDelimiter(Pattern pattern). For example, to parse comma-separated values, call scanner.useDelimiter(","). Be careful: changing the delimiter affects all subsequent read methods, including nextLine() behavior.
Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.