Reading from the console in Java involves one trap that catches every beginner: next() and nextLine() do not consume the same amount of input, and mixing them in the wrong order produces a program that appears to skip a line for no reason.
How do you read a line?
Create a Scanner over System.in and call nextLine().
import java.util.Scanner;
public class Greeting {
public static void main(String[] args) {
try (Scanner in = new Scanner(System.in)) {
System.out.print("What is your name? ");
String name = in.nextLine();
System.out.println("Hello, " + name);
}
}
}
The try-with-resources block closes the scanner when the block ends. Closing a scanner over System.in closes standard input for the whole program, so it matters if you read again later — in a small program it is not a practical problem, but it is good habit to write it this way from the start.
Why does my input get skipped?
Here is the trap. next() reads a token and leaves the line break in the input; nextLine() reads the rest of the line including the break.
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(); // reads "25" and leaves the newline
System.out.print("City: ");
String city = in.nextLine(); // returns "" — the newline was left behind
System.out.println("age=" + age + " city=[" + city + "]");
}
}
Type 25 and press enter, then type Paris, and the program prints city=[] — the city was swallowed by the leftover line break. There are three ways out, and the choice is yours.
- Read everything as text with
nextLine()and convert it yourself. This is the most predictable option. - Mix in the right order: read the number last, or consume the leftover break with
in.nextLine()after a numeric read. - Use
useDelimiter("\s+")when every input is whitespace-separated.
Scanner in = new Scanner(System.in);
System.out.print("Age: ");
int age = in.nextInt();
in.nextLine(); // consume the leftover newline
System.out.print("City: ");
String city = in.nextLine(); // now it works
Can the scanner read typed values directly?
Yes, and it is convenient when the input is well-formed: nextInt(), nextLong(), nextDouble(), nextBoolean() and next() for a single word. The risk is that they throw when the input is not what they expect.
import java.util.Scanner;
public class Typed {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
try {
System.out.print("Quantity: ");
int quantity = in.nextInt();
System.out.println("got " + quantity);
} catch (java.util.InputMismatchException e) {
System.out.println("That was not a whole number.");
}
}
}
An InputMismatchException is a runtime exception, so an unchecked input makes the program die with a stack trace unless you handle it. A more robust pattern is to ask whether the input is valid before reading it.
Scanner in = new Scanner(System.in);
System.out.print("Quantity: ");
if (in.hasNextInt()) {
int quantity = in.nextInt();
System.out.println("got " + quantity);
} else {
System.out.println("that was not a whole number");
}
How do I read several lines?
A loop over hasNextLine() reads an unknown number of lines until the input ends.
import java.util.Scanner;
public class Lines {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int lineNumber = 1;
while (in.hasNextLine()) {
String line = in.nextLine();
System.out.println(lineNumber++ + ": " + line);
}
}
}
For large inputs, a scanner is not the right tool: it parses character by character and is noticeably slower than buffered reading. A BufferedReader with readLine() is the standard alternative for anything substantial.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String line = reader.readLine();
How do you prompt without breaking the input?
Printing a prompt and then reading on the same line looks like the obvious way to do it, and it works — as long as the prompt is followed by a space and the reader knows to type on that line. The alternative is to print the prompt and then a newline, and to read the whole line as text, which is more predictable when the input might be pasted from somewhere else.
import java.util.Scanner;
public class Prompt {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.println("What is your name?");
String name = in.nextLine();
System.out.println("How old are you?");
String ageText = in.nextLine();
int age = Integer.parseInt(ageText);
System.out.println(name + " is " + age);
}
}
Reading the age as text and converting it yourself is the pattern that never trips over the newline problem, because every read consumes a whole line. It also gives you a place to handle a bad value: Integer.parseInt throws a NumberFormatException on text that is not a number, and catching that at the boundary is much better than letting it escape.
Does the same work apply to files?
Yes, and this is where a scanner earns its keep. Pointing one at a file gives you the same API for a completely different source, which is how you write code that reads from the console in a test and from a data file in production.
import java.nio.file.Path;
import java.util.Scanner;
try (Scanner in = new Scanner(Path.of("data.txt"))) {
while (in.hasNextLine()) {
System.out.println(in.nextLine().toUpperCase());
}
}
Swap System.in for a path and everything else stays the same. That is the real lesson of the scanner: it is a source-agnostic reader, and the console is just the easiest source to reach.
What should you use for large amounts of input?
Scanner is convenient, and it is the right choice for a program that asks a person a few questions. It is not the right choice for a file with a million lines, because Scanner parses with regular expressions and allocates more than the underlying stream needs to. For bulk input, BufferedReader with readLine is leaner and perfectly easy to use.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
public class BulkReading {
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(
new InputStreamReader(System.in, StandardCharsets.UTF_8));
String line;
int count = 0;
while ((line = reader.readLine()) != null) {
count++;
}
reader.close();
System.out.println("lines read : " + count);
}
}Two habits make this safe. Read the line into a variable and compare it with null rather than comparing to an empty string, because null is what marks the end of the input. And close the reader when you are done, or wrap it in a try-with-resources block so it closes itself even when something throws.
There is a middle ground worth knowing: Scanner also has a useDelimiter method that lets you treat a whole file as one long stream of tokens separated by commas or tabs. That is the quick way to read a simple CSV without pulling in a real parsing library - and it is still faster than parsing the file line by line with split.
Quick reference
next()reads one word and leaves the line break;nextLine()reads the whole line.- Mixing them in the wrong order silently skips a line.
- Typed reads throw
InputMismatchExceptionon bad input — check withhasNextInt()first. - Wrap the scanner in try-with-resources so it is closed.
- For large input, use a
BufferedReaderinstead.
Try it yourself: write a program that asks for a name and an age, then prints a greeting that includes both. Get the order wrong on purpose first so you can see the empty-line bug, then fix it in the two ways described above and confirm both behave correctly.