فخ nextInt / nextLine
في هذا الدرس 11 موضوعات
هذا الدرس متاح حاليًا باللغة الإنجليزية فقط، ولم تُنشر نسخته العربية بعد.
Lesson Overview
In the last lesson you read text and numbers with Scanner. Each program worked. Now put the two together in the most natural order: ask for an age (a number), then ask for a full name (a line). Run it, type your age, and watch what happens: the program never lets you type the name. It prints an empty name and finishes.
This is the best-known Scanner surprise, and almost every Java beginner hits it. Nothing is broken in Java. The program does exactly what it was told; what it was told is just not what you thought. Once you understand where the Enter key goes when you press it, the problem becomes obvious and the fix is one line.
Why this matters: the cause is a general fact about input, not just about Scanner. Input arrives as a stream of characters in order, a reader consumes it piece by piece, and what a method does not consume stays there for the next method. You will meet the same idea with files, sockets, and request bodies.
This lesson builds directly on "Reading input with Scanner".
Learning Objectives
By the end of this lesson you will be able to:
- Describe input as a stream of characters, including the invisible line break.
- Explain exactly what
nextInt()consumes and what it leaves behind. - Explain why a
nextLine()that followsnextInt()returns an empty string. - Fix the trap with one extra
nextLine()call. - Avoid the trap in two other ways: reorder the questions, or read every answer with
nextLine(). - Recognise the same symptom when mixing
nextDouble()ornext()withnextLine().
Written Explanation
The keyboard is a stream
When you type 30 and press Enter, the program does not receive a number. It receives characters, one after another: 3, 0, and then a line break that represents the Enter key. (On Windows the line break is two characters, and on Linux and macOS one, but Scanner treats them all as a line ending, so we write \n for "the line break".)
After you type 30, Enter, the input waiting to be read looks like this:
30\nThink of it as a ribbon of paper coming out of a machine. A read position marks where the reader is. Each Scanner method starts at the read position and moves it forward.
What nextInt() consumes
nextInt() skips any whitespace in front of the number, reads the token 30, and stops right after the last digit. It does not touch what follows:
30\n
^ read position is here, before the \nThe line break is still in the input. Nobody asked nextInt() to remove it, and it does not.
What nextLine() consumes
nextLine() has a different job. The Javadoc says it advances the scanner past the current line and returns the input that was skipped. In plain words: it returns everything from the read position up to the next line break, and then moves past the line break.
If the read position is right in front of a \n, "everything up to the line break" is nothing at all. So nextLine() returns the empty string "" immediately, without waiting for the user, and the read position moves past the \n.
That is the whole trap. Step by step for the program in the example below:
nextInt()reads30and leaves\nbehind.- The prompt
Full name:is printed. nextLine()finds the leftover\n, returns"", and the program carries on.- The user never gets the chance to type the name, because the scanner already had a line to give.
Rule: a
nextInt(),nextDouble(), ornext()reads a token and leaves the end of the line. AnextLine()right after one of them reads that leftover.
Fix 1: throw the leftover away
Call nextLine() once, immediately after the number, and ignore what it returns:
int age = in.nextInt();
in.nextLine(); // consumes the rest of the age line
String name = in.nextLine(); // now reads the real nameYou do not store the result. The call exists only to move the read position past the leftover line break. A short comment explaining the purpose is good style, because a bare in.nextLine(); looks like a mistake to a reader.
If the user types extra text after the number (30 years), that extra text is also part of the leftover and the extra nextLine() discards it too. That is useful: it keeps the next question clean.
Fix 2: change the order
The trap needs a number followed by a line. A line followed by a number is safe, because nextLine() consumes its own line break. In a program with several questions, putting the line questions first and the number questions last avoids the problem without any extra call. This is not always possible, though; a form often has to ask in a fixed order.
Fix 3: read every answer as a line
A third approach is to read everything with nextLine() and convert the text to numbers yourself. Then every read consumes its own line break and no leftover exists. The conversion method is the topic of the next lesson, Integer.parseInt. Many programmers prefer this approach for long forms, because it behaves the same for every question.
| Approach | Extra code | Best when |
|---|---|---|
Extra nextLine() after each number | One line per number | A few numbers mixed with lines |
| Reorder questions | None | Order is free |
| Read all as lines, then convert | A conversion per number | Long forms; the next lesson |
The same trap with other methods
The trap appears after any method that reads a token: next(), nextDouble(), nextBoolean(), and so on. It does not matter whether the number comes first or you read several numbers in a row. Only the nextLine() that follows a token read is affected. Two number reads in a row are fine, because both skip whitespace, which includes line breaks, in front of the token.
Examples
Example 1: the bug (simple)
import java.util.Scanner;
public class Trap {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.print("Age: ");
int age = in.nextInt();
System.out.print("Full name: ");
String name = in.nextLine();
System.out.println("[" + name + "] is " + age + " years old.");
}
}With the user typing 30 and pressing Enter, the program runs straight through:
Age: 30
Full name: [] is 30 years old.The user never typed a name. The square brackets in the output make the empty string visible: always print values inside markers like [ and ] while debugging, because an empty or space-filled string is invisible otherwise.
Example 2: the fix (practical)
import java.util.Scanner;
public class Fixed {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.print("Age: ");
int age = in.nextInt();
in.nextLine(); // throw away the rest of the age line
System.out.print("Full name: ");
String name = in.nextLine();
System.out.println("[" + name + "] is " + age + " years old.");
}
}Age: 30
Full name: Ana Smith
[Ana Smith] is 30 years old.The only change is the line with the comment. The extra nextLine() consumed the leftover \n, so the next nextLine() had to wait for real input.
Example 3: a hidden leftover (realistic)
The leftover is not always empty. Suppose a form reads two numbers on one line and then a note:
import java.util.Scanner;
public class Mixed {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.print("Two numbers: ");
int a = in.nextInt();
double b = in.nextDouble();
System.out.println("a=" + a + " b=" + b);
System.out.print("Note: ");
String note = in.nextLine();
System.out.println("note=[" + note + "]");
}
}If the user types 7 2.5 and Enter, the note comes back empty again:
Two numbers: 7 2.5
a=7 b=2.5
Note: note=[]Now suppose a careless user types all three things on the first line, 7 2.5 hello there. The "note" is the rest of that line, including the space in front of hello:
Two numbers: 7 2.5 hello there
a=7 b=2.5
Note: note=[ hello there]The program has not waited at all: the scanner had unread text on the current line, so nextLine() returned it at once. This is a real-world version of the trap, and the same extra nextLine() after the last number (or a .trim() on the result) is the cure. It also shows why you should never assume the leftover is exactly an empty line.
Backend Insight
Every protocol that reads text deals with this boundary problem: where does one piece of data end and the next begin? Web servers read HTTP requests as a stream, and the parser must consume exactly the right number of characters. A parser that leaves one character unread, or reads one too many, corrupts everything after it, in the same way our nextLine() read the leftover.
Two habits carry over from this lesson to server code:
- Know what each read consumes. A reading method defines where it starts and where it stops. Whoever reads next inherits the leftover.
- Make the bug visible. Printing
[and]around a value, or logging the raw input, is how you find a stray space or newline. Production logs for request handling use the same technique.
Common Mistakes
Mistake 1: Reading a line right after a number
- Incorrect:
nextInt()followed bynextLine()for a name. - Correct:
nextInt(), then one throw-awaynextLine(), then the realnextLine().
Mistake 2: Adding the extra nextLine() where it is not needed
If the program reads a line first and then a number, the extra call is not needed, and it would swallow the user's next input. Place it only after a token read that is followed by a line read.
Mistake 3: Calling the extra nextLine() after the wrong read
The extra call must come directly after the last number on that line, and before the next line read. Putting it before the number does nothing useful.
Mistake 4: Blaming the keyboard or Java
"Java skipped my question" is a misunderstanding. The input stream held a line break, and nextLine() correctly read it. Print the result inside brackets to see what you got.
Mistake 5: Assuming the leftover is always empty
If the user types extra text after the number, the leftover is not empty. The throw-away call discards it, which is usually what you want.
Exercises
Try each exercise yourself before opening the solutions.
Level A: Foundation
A1. After the user types 25 and Enter, what unread characters remain once nextInt() has finished?
أظهر الحل A1
A1. Only the line break (\n). nextInt() consumed the digits 2 and 5 and stopped.
A2. Which of these sequences has the trap: (a) nextLine(), nextInt(); (b) nextInt(), nextLine(); (c) nextInt(), nextInt(); (d) nextDouble(), nextLine()?
أظهر الحل A2
A2. (b) and (d). In (a) the line comes first, so it consumes its own break. In (c) both reads skip whitespace in front of the token.
A3. What does nextLine() return when the read position is directly before a line break?
أظهر الحل A3
A3. The empty string "". It does not wait for the user, because a line (an empty one) is already available.
Level B: Application
B1. This program should ask for a city and then a population, then a country. It has one bug. Find it and fix it.
System.out.print("Population: ");
int pop = in.nextInt();
System.out.print("Country: ");
String country = in.nextLine();أظهر الحل B1
B1. nextInt() leaves the line break behind, so the country comes back empty. Fix: add in.nextLine(); between the nextInt() and the country read:
int pop = in.nextInt();
in.nextLine(); // discard the leftover line break
System.out.print("Country: ");
String country = in.nextLine();B2. Reorder these questions so that the program needs no extra nextLine(): age (number), full name (line), height (decimal), street address (line).
أظهر الحل B2
B2. Lines first, numbers last: full name, street address, age, height. After the last number there is nothing more to read, so nothing is left to cause trouble. (Reading the name before the age also works, if you need to ask age second, but then the throw-away call returns.)
B3. A friend added in.nextLine(); before every nextInt() "to be safe". Explain what happens at the first prompt, and why that is a bug.
أظهر الحل B3
B3. The first nextLine() is called with no leftover in front of it, so it waits for the user to type a whole line, and the program appears to ask for the number twice. The extra call belongs after a number read and before a line read, not before every number.
Level C: Challenge
C1. Write a program that asks for a quantity (int), a product name (line), and a price (double), then prints 3 x Pen at 1.5. Use Fix 1 correctly. Where exactly does each throw-away call go?
أظهر الحل C1
C1.
System.out.print("Quantity: ");
int qty = in.nextInt();
in.nextLine(); // after the number, before the line
System.out.print("Product: ");
String name = in.nextLine(); // a line, consumes its own break
System.out.print("Price: ");
double price = in.nextDouble(); // last read; nothing follows it
System.out.println(qty + " x " + name + " at " + price);Only one throw-away call is needed, between the number and the line. The price read is last and is followed by no line read. (Not compiled separately; it uses the same calls as the compiled examples.)
C2. The user types 42 abc and Enter in response to nextInt(), followed by a nextLine() whose result is printed in brackets. What is printed, and why?
أظهر الحل C2
C2. The nextInt() reads 42, and the leftover is abc plus the line break. nextLine() returns abc (with a leading space), so [ abc] is printed. The program does not wait. This is the same behaviour as Example 3.
Mini Challenge
Build Pet registration. The program asks, in this order: the pet's name (line), age in years (int), the owner's full name (line), and weight in kg (double). Print a one-line summary. Run it with the throw-away call removed first and note which answer comes back wrong. Then add the one needed line.
Check your work: you can say which answer was skipped and why, and the final version has exactly one throw-away nextLine().
Summary & Next Lesson
Key takeaways
- Input is a stream of characters, and the Enter key is a line break character in it.
nextInt(),nextDouble(), andnext()read a token and leave the rest of the line, including the line break.- A
nextLine()that follows reads that leftover and returns it, often as an empty string. - Fix: one throw-away
in.nextLine();after the number. Alternatives: ask line questions first, or read everything as lines. - Print values inside brackets while debugging so empty and space-filled strings become visible.
Next lesson
Next is "Parsing strings into numbers". You will convert text into numbers with Integer.parseInt and Double.parseDouble, which is the third fix above, and see what happens when the text is not a number.
Self-check
- What character does
nextInt()leave in the input? - Why does the following
nextLine()return an empty string? - What is the one-line fix?
- Is the trap present when
nextLine()comes beforenextInt()? - Name one other method that leaves the line break behind.
أظهر الإجابات
- The line break from the Enter key.
- The read position is right before the leftover line break, so the "rest of the line" is empty.
- Call
in.nextLine();once right after the number read, ignoring the result. - No.
nextLine()consumes its own line break, so the number read after it starts clean. next()ornextDouble()(any token reader).
References
- Oracle, Java SE 21 API, Scanner. Supports tokens separated by whitespace by default,
nextLine()returning the rest of the current line and advancing past it, and number methods reading only a token. - All code in this lesson was compiled and run with JDK 21; the quoted program output is real. The platform detail about the line break being one or two characters is general knowledge not checked against a page for this lesson.
التطبيق
طبّق هذا الدرس بمختبر الوحدة وتمارينها واختبارها.
تعليم الدرس كمكتمل اختياري ويمكنك التراجع عنه في أي وقت. يبقى تقدمك محفوظًا في هذا المتصفح.