Reading input from the console
Every program so far has had its data typed into the source. That is fine for
learning a for loop and useless for anything else — a program nobody can talk
to is a program with one user.
This lesson makes your programs interactive. It is also where you meet the single most notorious beginner bug in Java, and rather than warning you about it afterwards, we are going to cause it on the second page.
Scanner
import java.util.Scanner;
public class Greet {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Customer name: ");
String name = scanner.nextLine();
System.out.println("Namaste, " + name);
}
}
System.in is the input stream connected to your keyboard, and it delivers raw
bytes. Scanner wraps it and turns those bytes into strings and numbers.
Note System.out.print rather than println for the prompt, so the cursor stays
on the same line. That is the difference between a program that looks finished
and one that does not.
The methods, and the distinction that matters
| Method | Reads | Leaves behind |
|---|---|---|
nextLine() |
Everything up to the newline | Nothing — consumes the newline |
next() |
One whitespace-separated token | The rest of the line, including the newline |
nextInt() |
One token, as an int |
The rest of the line, including the newline |
nextLong() |
One token, as a long |
The rest of the line |
nextDouble() |
One token, as a double |
The rest of the line |
nextBoolean() |
true or false |
The rest of the line |
hasNextLine() |
— | Asks whether another line exists |
hasNextInt() |
— | Asks whether the next token parses as an int |
Read that middle column twice. nextLine() consumes the newline; nothing else
does. Every trap below comes from that one fact.
The bug
Scanner scanner = new Scanner(System.in);
System.out.print("Tiffins taken: ");
int tiffins = scanner.nextInt();
System.out.print("Customer name: ");
String name = scanner.nextLine();
System.out.println("tiffins = " + tiffins);
System.out.println("name = [" + name + "]");
Type 26, press Enter, and then try to type a name:
Tiffins taken: 26
Customer name: tiffins = 26
name = []
It never waited. The name is empty, and the program did not pause to ask.
Here is exactly what happened. You typed 26 and Enter, so the input buffer
holds 26\n. nextInt() took the 26 and stopped, leaving the \n sitting
there. nextLine() then read everything up to the next newline — which was
immediately, with nothing before it. It returned an empty string without ever
needing your keyboard.
Every mixed nextInt() / nextLine() program has this bug until it is fixed.
Two fixes, and only one of them is good.
The patch everybody writes, an extra nextLine() to swallow the leftover
newline:
int tiffins = scanner.nextInt();
scanner.nextLine(); // discard the rest of the line
String name = scanner.nextLine();
Works. But now every nextInt() needs a matching throwaway call, and forgetting
one reintroduces the bug somewhere you are not looking.
The fix worth adopting: read lines, parse them yourself.
System.out.print("Tiffins taken: ");
int tiffins = Integer.parseInt(scanner.nextLine().strip());
System.out.print("Customer name: ");
String name = scanner.nextLine().strip();
Use nextLine() for everything, and convert with Integer.parseInt,
Long.parseLong or Double.parseDouble. One method, one line of input per
call, no hidden state. The strip() handles the spaces people type without
noticing.
This is not a beginner's simplification — it is what production code does, because it also makes the error handling possible, which is the next section.
When the user types rubbish
They will. nextInt() on abc does this:
java.util.InputMismatchException
No message at all — getMessage() returns null. And the bad token is not
consumed, so a naive retry loop spins forever on the same text.
Integer.parseInt fails more usefully:
java.lang.NumberFormatException: For input string: "abc"
That one tells you what it choked on, which matters when the input came from a file with four thousand rows.
Two ways to handle it. Ask before you read, using the hasNextInt family:
if (scanner.hasNextInt()) {
int n = scanner.nextInt();
}
Or try to parse and catch the failure, which works with the read-lines approach and is the one that scales:
static int readInt(Scanner scanner, String prompt, int min, int max) {
while (true) {
System.out.print(prompt);
String line = scanner.nextLine().strip();
try {
int value = Integer.parseInt(line);
if (value < min || value > max) {
System.out.println(" Enter a number between " + min + " and " + max + ".");
continue;
}
return value;
} catch (NumberFormatException e) {
System.out.println(" [" + line + "] is not a whole number.");
}
}
}
Tiffins taken: twenty six
[twenty six] is not a whole number.
Tiffins taken: 99
Enter a number between 0 and 62.
Tiffins taken: 26
Customer name: Priya Deshmukh
try/catch is module 7's subject and this is the whole shape of it: attempt
the risky thing, and say something useful when it fails. Two details worth
copying. The range check is a separate message from the parse failure, because
"99 is not a number" would be a lie. And the invalid input is echoed back in
brackets, so an invisible trailing space is visible in the message.
Write readInt once and reuse it. Every command-line program you write from
here on needs it, and the capstone has a version of exactly this.
Reading until there is no more
A program fed from a file or a pipe has to know when to stop:
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
if (line.isBlank()) {
continue;
}
process(line);
}
hasNextLine() returns false at end of input. Calling nextLine() anyway
throws:
java.util.NoSuchElementException: No line found
That is what you get when a program expects input and is run with none — a
common way for a working program to fail in a CI pipeline. On a terminal, end of
input is Ctrl+D on macOS and Linux, Ctrl+Z then Enter on Windows.
This also means your program works with a pipe for free:
java Report.java < deliveries.txt
Which makes it testable without typing anything, and is worth knowing now.
Four practical points
Do not close a Scanner wrapping System.in. Closing it closes
System.in itself, and any later Scanner throws NoSuchElementException. This
is the one place where the usual "always close what you open" advice is wrong.
Create one Scanner for the program and pass it around.
One Scanner per program. Two scanners on System.in will steal buffered
input from each other, producing behaviour that looks random.
System.console() returns null in an IDE. It is the API for reading a
password without echoing it, and IntelliJ does not provide a real console, so it
fails only when run from the IDE and works from a terminal. Use Scanner unless
you specifically need hidden input.
Locale affects nextDouble(). On a system configured for a locale that uses
a comma as the decimal separator, nextDouble() rejects 3.14. Another reason
to read lines and use Double.parseDouble, which always expects a dot.
Check your work
Why does nextLine() after nextInt() return an empty string?
nextInt() consumes the digits and leaves the newline in the buffer.
nextLine() reads up to the next newline, finds it immediately, and returns the
empty text before it.
What are the two fixes, and which is better? An extra throwaway
scanner.nextLine() after every numeric read, or reading everything with
nextLine() and converting with Integer.parseInt. The second is better: it has
no hidden state to forget and it gives you a catchable
NumberFormatException.
What does nextInt() throw on abc, and why is that awkward?
InputMismatchException, with a null message, and the offending token is left in
the buffer — so a retry loop that does not consume it spins forever.
What does Integer.parseInt("abc") throw?
NumberFormatException: For input string: "abc" — which names the input, so it
is far more useful in a log.
What happens if you call nextLine() at end of input?
NoSuchElementException: No line found. Guard with hasNextLine().
Why should you not close a Scanner on System.in? It closes System.in
as well, so nothing can read input afterwards.
Practice 2, the two-question program. The working version reads both answers as lines:
Scanner scanner = new Scanner(System.in);
System.out.print("Tiffins taken: ");
int tiffins = Integer.parseInt(scanner.nextLine().strip());
System.out.print("Customer name: ");
String name = scanner.nextLine().strip();
System.out.printf("%s owes Rs %d.%02d%n", name, tiffins * 8235 / 100, tiffins * 8235 % 100);
Practice 4, the menu loop.
boolean running = true;
while (running) {
System.out.println("""
1 Add a delivery
2 Show the total
3 Quit""");
String choice = scanner.nextLine().strip();
switch (choice) {
case "1" -> deliveries++;
case "2" -> System.out.println("Deliveries: " + deliveries);
case "3" -> running = false;
default -> System.out.println("Unknown option [" + choice + "]");
}
}
Switching on the String rather than parsing an int first is deliberate: an
unknown option is then an ordinary default branch instead of an exception, and
a blank line does not crash the program. The running flag beats break here
because break inside a switch would leave the switch, not the loop — which
is exactly the kind of quiet mistake the arrow form was introduced to prevent.
Practice
-
Cause the bug. Write the
nextInt()thennextLine()program and watch it skip the name. Then fix it both ways — with a throwawaynextLine(), and by reading lines and parsing. Keep the second. -
A two-question bill. Ask for a customer name and a number of tiffins, then print what they owe at Rs 82.35 each, formatted in rupees and paise. No
doubleanywhere. -
Make
readIntyour own. Copy the version above, then extend it: allow a blank line to mean "use the default" and take that default as a parameter. Test it with a blank line, a negative number,9999,12abcand a line of spaces. -
A menu loop.
1 add a delivery,2 show the total,3 quit. Usedo/whileor a flag, handle an unknown option without exiting, and make sure pressing Enter on an empty line does not crash it. -
Read until end of input. Write a program that reads lines until there are none, ignores blank ones, and prints how many non-blank lines it saw and the longest. Run it twice: once by typing and ending with
Ctrl+D, and once asjava Count.java < somefile.txt. The second is how you will test every command-line program you write. -
Harder — a delivery log. Read lines of the form
Priya,26until end of input, one customer per line. Reject a line with the wrong number of fields or a non-numeric count by printing which line number was bad and carrying on rather than stopping. At the end, print each customer, their tiffin count, and the grand total. Carrying on after a bad row rather than dying is the difference between a script and a tool.
That is module two. You can declare and convert types without losing money to a
double, handle strings without the == trap, use both forms of switch, work
with arrays and know their limits, and write a program a person can actually use.
Next module: object-oriented Java — classes, records, enums, and the Java 21 pattern matching that makes them worth having.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship