Scanner, Conversion & Scope
Reading multiple inputs safely, converting strings to numbers, and understanding where variables live
Before you start: Lesson 1e introduced the buffer trap and the
nextLine()fix. This lesson builds on that — make sure you can explain whatnextInt()leaves in the buffer. If you can’t, re-read lesson-1e first.Lab 3 maps directly to this lesson — every section below corresponds to a checkpoint.
Learning Objectives
After this lesson, you will be able to:
- [CP1] Read multiple values from the keyboard using only
nextLine(), then convert each string tointordoubleusingInteger.parseInt()orDouble.parseDouble()— without triggering the buffer trap - [CP2] Compute time components from a minute total using integer division and modulo, predicting the truncated result of each operation before running the code
- [CP3] Force a decimal result from integer variables using
(double)cast, and format that result to exactly two decimal places usingSystem.out.printf() - [Depth] Trace the scope of a variable — identify its opening brace and determine whether a later line of code is inside or outside that scope
Part 1 — Reading Multiple Inputs [→ CP1]
Earlier you saw the buffer trap once. Now you will write programs that read three or more values in a row — and the trap becomes impossible to avoid unless you know exactly what to do.
Why nextInt() breaks nextLine()
When you type 25 and press Enter, the input buffer holds:
25\n
nextInt() consumes 25 and stops. The \n stays. The very next nextLine() sees that \n and returns "" immediately — no waiting, no reading your actual input.
// THIS IS BROKEN — do not write this
Scanner sc = new Scanner(System.in);
int age = sc.nextInt(); // reads 25, leaves \n in buffer
String name = sc.nextLine(); // reads "" — NOT "Alice"
Fix 1: Drain the buffer (use this for now)
After any nextInt(), nextDouble(), or next() call, immediately add a bare sc.nextLine() that reads and discards the leftover \n. Don’t save the result.
Scanner sc = new Scanner(System.in);
int age = sc.nextInt(); // reads 25, leaves \n in buffer
sc.nextLine(); // drain — discards the leftover \n, save nothing
String name = sc.nextLine(); // now reads "Alice" correctly
One drain call per non-line read. Put it on the very next line so it’s obvious what it’s for.
Fix 2: Use nextLine() for everything (you’ll learn this later)
Read every value as a string, then convert with parseInt or parseDouble. No leftover newlines — nextLine() always consumes the full line including the \n.
Scanner sc = new Scanner(System.in);
String ageText = sc.nextLine(); // reads "25", buffer now empty
String nameText = sc.nextLine(); // reads "Alice", buffer now empty
int age = Integer.parseInt(ageText); // "25" → 25
String name = nameText; // already a String
This is cleaner when reading many values, but gets verbose. You’ll see a better version when we cover arrays.
For this course right now: Use Fix 1 —
nextInt()(ornextDouble()) followed immediately by a baresc.nextLine()to drain the buffer. You can use Fix 2 if you prefer. Either is fine as long as your program produces the correct output.
The conversion methods
| You want | Method | What it does |
|---|---|---|
int |
Integer.parseInt(text) |
Converts "42" → 42. Crashes on "42.0" or "hello". |
double |
Double.parseDouble(text) |
Converts "3.14" → 3.14. Works on integers too: "5" → 5.0. |
int miles = Integer.parseInt(sc.nextLine()); // "350" → 350
int mpg = Integer.parseInt(sc.nextLine()); // "28" → 28
double gasPrice = Double.parseDouble(sc.nextLine()); // "3.49" → 3.49
Rule for this course: After any
nextInt(),nextDouble(), ornext(), add a baresc.nextLine()immediately after to drain the buffer. Or usenextLine()for all reads. Either approach is fine — just be consistent and test your output.
Part 2 — Integer Division and Modulo [→ CP2]
You covered these in lesson-1e. This is the pattern you need for Lab 3 checkpoint 2 — know it cold.
int totalMinutes = 210;
int hours = totalMinutes / 60; // 3 (truncates — 3.5 becomes 3)
int minutes = totalMinutes % 60; // 30 (remainder after 3 full hours)
The two operators are always paired for time conversions:
/gives the whole part%gives the leftover
What does % actually compute?
a % b = a - (a / b) * b using integer division throughout. So 210 % 60 = 210 - 3*60 = 210 - 180 = 30. The result is always in the range [0, b-1].
Common uses: hours/minutes/seconds from total seconds, even/odd (n % 2 == 0), wrapping around a ring of values.
Part 3 — Casting and printf [→ CP3]
This is the gap-filler for Lab 3. Lesson 2b covers the full theory. Here you learn just enough to complete checkpoint 3.
The problem: integer division truncates
int miles = 100;
int mpg = 30;
double gallons = miles / mpg; // 100/30 = 3 (truncated!), stored as 3.0
miles / mpg is evaluated first — both are int, so Java uses integer division and gets 3. That 3 is then stored as 3.0. You never got the real answer 3.333....
The fix: (double) cast
double gallons = (double) miles / mpg; // (double)100 / 30 = 100.0 / 30 = 3.333...
(double) applied to miles converts that one value to 100.0. Now the division is double / int, which Java promotes to double / double, giving 3.333....
Where it goes: the cast attaches to the variable immediately to its right. (double) miles / mpg casts miles then divides. (double) (miles / mpg) divides first (truncated) then casts — wrong order, wrong answer.
Formatting output with printf
System.out.printf() formats values with exact control over decimal places.
double cost = 3.499999;
System.out.printf("Fuel cost: $%.2f%n", cost);
// output: Fuel cost: $3.50
| Format specifier | What it does |
|---|---|
%.2f |
Print a double rounded to 2 decimal places |
%d |
Print an int |
%s |
Print a String |
%n |
Newline (use instead of \n in printf) |
Multiple values:
System.out.printf("Drive: %d hr %d min%n", hours, minutes);
System.out.printf("Gallons: %.2f%n", gallons);
System.out.printf("Cost: $%.2f%n", cost);
Part 4 — Variable Scope [→ Depth]
Every variable in Java has a scope — the region of code where it is visible. The scope starts at the variable’s declaration and ends at the closing } of the block that contains it.
public class App {
public static void main(String[] args) {
int x = 10; // x is in scope here
{
int y = 20; // y is in scope here
System.out.println(x + y); // fine — x is visible inside
}
System.out.println(x); // fine — still inside main
System.out.println(y); // COMPILE ERROR — y is gone
}
}
The outer scope can’t see the inner scope’s variables. The inner scope can see the outer scope’s variables. This is true for any block: if, else, for, while.
The scope trap
// BROKEN
Scanner sc = new Scanner(System.in);
String input = sc.nextLine();
if (input.equals("yes")) {
String reply = "Going!";
}
System.out.println(reply); // COMPILE ERROR — reply is gone after the }
// FIXED — declare before the block
String reply = "";
if (input.equals("yes")) {
reply = "Going!";
}
System.out.println(reply); // works — reply was declared in outer scope
Rule: If you need a variable after an
ifor loop, declare it outside and above that block — not inside it.
Putting It Together: Lab 3 Pattern
Here is the template for checkpoint 1. Every line has a job:
import java.util.Scanner;
public class App {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// Read all input first — use nextLine() only
int miles = Integer.parseInt(sc.nextLine());
int mpg = Integer.parseInt(sc.nextLine());
double gasPrice = Double.parseDouble(sc.nextLine());
// Echo inputs back (CP1)
System.out.println("=== Road Trip Fuel Planner ===");
System.out.println("Distance: " + miles + " miles");
System.out.println("Efficiency: " + mpg + " mpg");
System.out.printf("Gas price: $%.2f/gal%n", gasPrice);
// CP2: time components using integer division + modulo
int driveHours = miles / 60;
int driveMinutes = miles % 60;
System.out.printf("Drive time: %d hr %d min%n", driveHours, driveMinutes);
// CP3: decimal division using (double) cast + printf
double gallons = (double) miles / mpg;
double cost = gallons * gasPrice;
System.out.printf("Gallons needed: %.2f%n", gallons);
System.out.printf("Fuel cost: $%.2f%n", cost);
}
}
Run with 350, 28, 3.49 — you should get:
=== Road Trip Fuel Planner ===
Distance: 350 miles
Efficiency: 28 mpg
Gas price: $3.49/gal
Drive time: 5 hr 50 min
Gallons needed: 12.50
Fuel cost: $43.63
int miles = sc.nextInt(); then int mpg = Integer.parseInt(sc.nextLine());. What is the value of mpg when the user types 350 then 28?nextInt() reads 350 but leaves \n in the buffer. The next nextLine() reads that empty newline and returns "". Then Integer.parseInt("") throws a NumberFormatException. Fix 1: add a bare sc.nextLine(); right after nextInt() to drain the buffer. Fix 2: use nextLine() for all reads and parse manually.
(double) miles / mpg compute differently than miles / mpg when miles = 100 and mpg = 30?miles to 100.0 before the division. Now it's 100.0 / 30 — double divided by int — which Java promotes to 100.0 / 30.0 = 3.333.... Without the cast, 100 / 30 is int divided by int → 3.
What You Learned
- Read every input with
nextLine()— never callnextInt()ornextDouble()in lab code - Convert with
Integer.parseInt()(forint) orDouble.parseDouble()(fordouble) - Integer division
/and modulo%are paired:/gives the whole,%gives the remainder (double)cast forces decimal division: put it on the numerator, before the/System.out.printf("%.2f", value)formats a double to exactly 2 decimal places- Variables live from their declaration to the closing
}of their block — declare outside if you need them outside
What Comes Next
Next (Lesson 2b): Arithmetic expressions and operator precedence — why 2 + 3 * 4 = 14, the full rules for type promotion, and the String concatenation trap.
Lab 3 Checkpoint Guide
| Checkpoint | What it tests | Lesson section |
|---|---|---|
| CP1 | Scanner.nextLine() in source; parseInt/parseDouble; output echoes input |
Part 1 |
| CP2 | Integer division (/) and modulo (%) in source; correct hour/minute output |
Part 2 |
| CP3 | (double) cast in source; printf in source; fuel cost formatted to 2 decimal places |
Part 3 |