java-foundations 20 min read

Scanner, Conversion & Scope

Reading multiple inputs safely, converting strings to numbers, and understanding where variables live

Reading: Reges & Stepp: Ch. 2 §2.1–2.3

Before you start: Lesson 1e introduced the buffer trap and the nextLine() fix. This lesson builds on that — make sure you can explain what nextInt() 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 to int or double using Integer.parseInt() or Double.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 using System.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() (or nextDouble()) followed immediately by a bare sc.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(), or next(), add a bare sc.nextLine() immediately after to drain the buffer. Or use nextLine() 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 if or 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

Check Your Understanding — CP1
A student writes int miles = sc.nextInt(); then int mpg = Integer.parseInt(sc.nextLine());. What is the value of mpg when the user types 350 then 28?
A28
B0
CNumberFormatException — the program crashes
D350
Answer: C. 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.

Check Your Understanding — CP3
What does (double) miles / mpg compute differently than miles / mpg when miles = 100 and mpg = 30?
ANothing — they produce the same result
Bmiles / mpg gives 3 (truncated); (double) miles / mpg gives 3.333...
C(double) miles / mpg gives 3.0 (same truncation, different type)
DThe cast applies to the result of miles / mpg, not to miles alone
Answer: B. The cast converts 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 call nextInt() or nextDouble() in lab code
  • Convert with Integer.parseInt() (for int) or Double.parseDouble() (for double)
  • 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