Casts, the Remainder Operator, and the Scanner Bug

Reading a stack trace, converting a double to an int with a cast, using the remainder operator to split a quantity into units, the structure of a complete conversion program, and the Scanner bug that eats your input. Follows Think Java 2e, Chapter 3 (Input and Output), Sections 3.6-3.10, pp. 40-46, cross-referenced against The Java Tutorials — Assignment, Arithmetic, and Unary Operators.

Subject: Java · 65 slides · code lesson

Open the interactive version of this deck

What this lesson covers

The lesson, slide by slide

1. Casts, the Remainder Operator, and the Scanner Bug

Title

Think Java 2e · Chapter 3 · Input and Output

Sections 3.6-3.10 · pp. 40-46

2. What you will be able to do

Objectives

This lesson follows Think Java 2e, Chapter 3 (Input and Output), Sections 3.6-3.10, pp. 40-46. Everything on these slides can be checked against those pages.

1. Read a stack trace: name the exception, find where it happened, and say which line to look at first.

2. Convert a double to an int with a type cast, and predict what happens to the fractional part.

3. Say why casting takes precedence over arithmetic, and use parentheses to control it.

4. Use the remainder operator to split a quantity into larger and smaller units.

5. Lay out a complete program: declarations at the top, each step separated and commented.

6. Explain the Scanner bug in terms of a stream of characters, and fix it with an extra nextLine.

3. Retrieve before you read

Warm-up

One fact from the previous lesson, and one from Chapter 2.

Discussion prompt

What does the format specifier %d require of the value it is given? And what does 7 / 2 evaluate to when both operands are ints?

Hint: One is about types at run time; one is about division.

Answer:

%d requires an integer — handing it a double throws IllegalFormatConversionException at run time. And 7 / 2 is 3, because integer division rounds toward zero.

Both come back immediately. This lesson opens with a printf mistake that fails at run time, and then spends two sections on converting between doubles and ints deliberately rather than by accident.

4. Where this lesson is going

Concept

Everything here serves one program: a converter that takes centimetres from the keyboard and reports feet and inches. Getting there needs a way to turn a double into an int, a way to get a remainder, and the ability to read the error messages you will hit along the way.

Figure (svg): A pipeline showing centimetres read in, divided into inches, then split into feet and remaining inches, then formatted

Downey & Mayfield, Think Java, 2nd edition (Green Tea Press / O'Reilly, 2020) — Think Java 2e, Chapter 3 (Input and Output), Sections 3.6-3.10, pp. 40-46 — Sections 3.6-3.10, printed pages 40-46.

5. Reading error messages

Section

Section 3.6

6. A printf mistake the compiler cannot catch

Concept

The values you pass to printf are separated by commas. If you are used to using the + operator to concatenate strings, you might write this by accident — and it is legal Java, so the compiler will not catch it.

System.out.printf("inches = %d" + inch);   // error at run time
stepwhat happens
concatenation happens firstif inch is 100, the result is "inches = %d100"
printf receives that one stringa format string, and no values to format
printf reaches %dthere is no value for it
resultMissingFormatArgumentException at run time

The problem is that concatenation happens before printf executes. printf gets a format string but no values, so when it reaches %d it does not know what to substitute.

7. Reading the stack trace

Notation

This looks like a wall of unfamiliar names. It has a strict structure, and only two of its lines matter to you today.

Annotate

  • Line 1 — the name of the exception. MissingFormatArgumentException, followed by the detail: Format specifier '%d'. That means it does not know what value to substitute for %d.
  • The middle lines are the Java library's own methods — Formatter.format, PrintStream.format, PrintStream.printf. You have no reason to know these, and you did not write them.
  • The last line is yours. Example.main(Example.java:10) — the method you actually wrote, the file it is in, and the line number.
  • So the rule is: read the FIRST line to see what happened, and the LAST line to see where. Everything between is the path the call took to get there.
  • The trace reads bottom-up in time. Your main ran, which called printf, which called format, which called format — and the innermost one is where it failed.
  • And remember Section 2.9's warning: where the error is discovered is not always where it was caused. Line 10 is where printf was called; the mistake is the + on that line.

In some IDEs you can click the error message and jump to the line. That is convenient and it is not a substitute for reading the first line, which is the only place that says what went wrong.

8. Diagnosing the MissingFormatArgumentException

Worked example

Apply the two-line rule and then reason about the cause, which is not on the line the trace names.

int inch = 100;
System.out.printf("inches = %d" + inch);
stepwhat you learnfrom where
what happenedMissingFormatArgumentException, specifier %dthe first line
whereExample.main, Example.java line 10the last line
what is on that linea printf with a + in itreading your own code
the causeconcatenation ran before printf, so no values were passedreasoning

Read the first line for the exception name and detail.

Why: MissingFormatArgumentException — a format specifier had no matching value.

Read the last line for your own method and line number.

Why: Example.main at line 10. Ignore the library frames in between.

Look at that line and ask what it passes to printf.

Why: One argument: the result of concatenating the format string with inch.

Fix it by passing the value as a separate argument.

Why: Replace the + with a comma.

Verify: Change the line to System.out.printf("inches = %d%n", inch); and run it — expect "inches = 100".

Why: If you still get an exception, check you replaced the plus with a comma rather than adding one: printf takes the format string first, then the values.

9. Which line will throw?

Prediction

All four compile. One fails at run time.

int x = 5;
System.out.println("x = " + x);
System.out.printf("x = %d%n", x);
System.out.printf("x = %d%n" + x);
linearguments to the callresult
println("x = " + x)one stringx = 5
printf("x = %d%n", x)format string + one valuex = 5
printf("x = %d%n" + x)one string, no valuesthrows

Predict first

Which call throws an exception at run time?

  • System.out.printf("x = %d%n" + x);
  • System.out.println("x = " + x);
  • System.out.printf("x = %d%n", x);
  • None of them

Correct: System.out.printf("x = %d%n" + x);

Why: Concatenation happens before printf runs, so printf receives a single string containing a %d specifier and no values to substitute into it, and throws MissingFormatArgumentException. The println version is fine because println genuinely takes one already-assembled string.

10. Plus or comma: which does printf want?

Concept

The confusion is understandable, because println really does want a +. The two methods take their arguments in completely different ways.

methodhow many argumentshow values are joined
printlnexactly oneyou build it yourself with +
printfa format string, then one per specifierprintf substitutes them for you

So println("x = " + x); and printf("x = %d%n", x); do the same job by opposite means. Mixing them — printf("x = %d" + x) — produces a format string with no values, which is exactly the exception above.

11. Joining printf's values with plus

Trap

The trap

The habit from println, carried over to printf where it does not belong.

System.out.printf("inches = %d" + inch);
printf receivesspecifiers in itvalues supplied
"inches = %d100"one (%d)none
result—MissingFormatArgumentException

Legal Java, compiles cleanly, throws at run time. The 100 is even visible in the string printf received — it just arrived as text rather than as a value to format.

The fix

A comma, so the value arrives as a separate argument.

System.out.printf("inches = %d%n", inch);
printf receivesspecifiersvaluesresult
"inches = %d%n"one (%d)one (inch)inches = 100

One specifier, one value. If you ever see a %d appear literally in your output, this is why: printf had a specifier and nothing to put in it, or the string never reached printf as a format string at all.

12. Which line of the trace do you read first?

Notation

A different exception, the same structure.

Annotate

  • First line: what happened. ArithmeticException, and specifically division by zero. This is the only line that says what the problem is.
  • Last line: where it started. Convert.main at line 11 — the line of yours that began the chain.
  • The middle line is also yours here. Convert.compute at line 22 is where the division actually happened, and it is the most useful line of all — the deepest frame you wrote.
  • The rule refined: read the first line for what, then scan upward from the bottom for the deepest frame in YOUR code. Library frames above that are the path, not the problem.

As your programs grow past one method, this refinement matters. The last line tells you where the chain began; the deepest frame you wrote tells you where it broke.

13. Which kind of error is each?

Definition probe

Chapter 2's three-way classification, applied to this lesson's mistakes.

Sort into buckets

Sort each mistake.

compile-time
forgetting the import for Scanner
run-time
using + instead of a comma in printf; using %d for a double
logic
printing a percentage as 0 because of integer division
ct
The compiler cannot resolve a name, so it refuses to build the program and nothing runs.
rt
Legal Java that compiles cleanly and then fails while running, with an exception naming what went wrong. Both format-string mistakes are of this kind, because the compiler never checks a format string against its values.
lg
It compiles, it runs, it produces output — and the output is wrong. No tool reports anything.

14. Why can't the compiler check format strings?

Socratic

It checks so much else. Ask why not this.

Discussion prompt

The compiler catches a missing semicolon and a type mismatch in an assignment. Why can it not catch printf("%d", 3.0), where the specifier and the value plainly disagree?

Hint: What is the format string, as far as the compiler is concerned?

Answer:

Because the format string is just a String. printf's declared signature says it takes a String and some values; nothing in the type system says the contents of that String constrain the values.

And the string need not be a literal at all — it could be read from a file or built at run time. The compiler would have to interpret the contents of a value, which is a different kind of checking from anything it does elsewhere.

Modern tools do check it, as a special case: IDEs and linters know printf specifically and warn you. That is worth knowing as a general pattern — when a language cannot express a constraint, tools grow to check it anyway.

15. Type cast operators

Section

Section 3.7

16. Casting converts a value from one type to another

Concept

Java converts an int to a double automatically, since no information is lost. Going the other way would lose the decimal places, so Java does not do it automatically — it wants you to be aware of the loss. The way to ask for it is a type cast.

inch = cm / CM_PER_INCH;      // error: possible lossy conversion

double pi = 3.14159;
int x = (int) pi;             // x gets the value 3
conversionautomatic?why
int to doubleyesnothing is lost — 3 becomes 3.0
double to intnothe fractional part would be discarded
(int) piexplicit castyou have said you accept the loss

type cast — An operator that converts a value from one type to another. Written as the target type in parentheses, used as a prefix.

It is called a cast because it moulds a value from one type into another. The syntax is to put the name of the type in parentheses and use it as an operator.

17. Casting throws the fraction away

Picture it

Like integer division, casting to an integer always rounds toward zero — even if the fractional part is 0.999999.

Figure (svg): A rule card showing that casting a double to an int discards the fractional part rather than rounding to nearest

Note the negative case. Toward zero moves a negative value up, so (int) -3.9 is -3 rather than -4. Chapter 4 shows how to round to the closest integer, which is a different operation entirely.

18. Casting takes precedence over arithmetic

Worked example

A cast binds more tightly than * or /, which produces a result most people do not expect the first time.

double pi = 3.14159;
double x = (int) pi * 20.0;    // 60.0, not 62.8318
stepexpressionvalue
as written(int) pi * 20.0—
the cast binds first3 * 20.0pi became the int 3
then the multiplication60.0an int times a double gives a double
what you may have wanted(int) (pi * 20.0)62

Notice that a cast is an operator, with its own precedence.

Why: Type casting takes precedence over arithmetic operations.

So (int) pi is evaluated before the multiplication.

Why: pi becomes 3, losing 0.14159 before anything else happens.

Multiply the resulting 3 by 20.0.

Why: 60.0 — a double, because one operand is a double.

To cast the whole product instead, parenthesise it.

Why: (int) (pi * 20.0) multiplies first, giving 62.8318, then casts to 62.

Verify: Print both (int) pi * 20.0 and (int) (pi * 20.0) and expect 60.0 and 62.

Why: If they came out equal, check your parentheses — the difference between these two is entirely a matter of where the cast applies.

This is the same lesson as the parentheses in Chapter 2's "Total: " + (a + b): an operator you did not think of as an operator still has a precedence.

19. What is the value of x?

Prediction

Casting rounds toward zero.

double d = 9.99;
int x = (int) d;
valueoperationresult
9.99(int) — discard the fraction9
—not rounding to nearestnot 10

Predict first

What value does x hold?

  • 9
  • 10
  • 9.99
  • a compile error

Correct: 9

Why: Casting to an integer always rounds toward zero, so it simply throws away the fractional part — 9.99 becomes 9, not 10. This is the same behaviour as integer division, and it is deliberate: Java wants truncation to be predictable rather than to guess at your intent.

20. Casting to inches, correctly

Concept

With the precedence rule understood, the conversion the chapter has been building toward becomes straightforward.

inch = (int) (cm / CM_PER_INCH);
System.out.printf("%f cm = %d in\n", cm, inch);
stepwhy
cm / CM_PER_INCHfloating-point division — CM_PER_INCH is a double
the parenthesesforce the division to happen before the cast
(int)converts the result, rounding toward zero
%d for inchinch is now an int, so %d is the right specifier

The parentheses after the cast operator require the division to happen before the type cast. Without them, (int) cm / CM_PER_INCH would truncate cm first and then divide — a different and wrong answer.

21. Casting the wrong part of the expression

Trap

The trap

Cast applied too early, because the parentheses are missing.

double cm = 254.0;
final double CM_PER_INCH = 2.54;
int inch = (int) cm / CM_PER_INCH;   // will not even compile
stepexpressiontype
the cast binds first(int) cmint — 254
then the division254 / 2.54double — 100.0
assign to an intint inch = 100.0;error: possible lossy conversion

Here the mistake produces a compile error, which is lucky. Change cm to a value with a fractional part and the same shape of mistake would silently give a wrong answer instead.

The fix

Parenthesise the whole expression so the cast applies to the final result.

double cm = 254.0;
final double CM_PER_INCH = 2.54;
int inch = (int) (cm / CM_PER_INCH);   // 100
stepexpressiontype
the parenthesescm / CM_PER_INCHdouble — 100.0
then the cast(int) 100.0int — 100
assignint inch = 100;fine

The rule to say out loud: a cast grabs the smallest thing to its right. If you want it to apply to more than one value, you have to say so with parentheses.

22. Where does the cast apply?

Prediction

A cast takes precedence over arithmetic.

double p = 3.9;
System.out.println((int) p * 2);
System.out.println((int) (p * 2));
expressionorderresult
(int) p * 2cast first: 3, then 3 * 26
(int) (p * 2)multiply first: 7.8, then cast7

Predict first

What two values are printed?

  • 6 then 7
  • 7 then 7
  • 6 then 6
  • 7 then 6

Correct: 6 then 7

Why: In the first line the cast binds to p alone, truncating 3.9 to 3 before multiplying, giving 6. In the second the parentheses force the multiplication first, giving 7.8, which the cast then truncates to 7. The difference is entirely in where the cast applies.

23. Which cast is illegal?

Elimination

In order to use a cast operator, the types must be compatible.

Eliminate the wrong options

Rule out the three that work and keep the one that does not.

  • A. (int) 3.99
  • B. (int) "3"
  • C. (double) 5
  • D. (int) -2.7

Survives elimination: B

Why: You cannot cast a String to an int, because a string is not a number — the types are not compatible and the compiler reports incompatible types. The string "3" is the character 3, not the value 3, which is the same distinction Chapter 2 drew. Converting text to a number is a different operation entirely, and needs a method rather than a cast.

24. What does casting a negative number do?

Edge cases

The phrase is 'rounds toward zero', not 'rounds down'. Test the difference.

Discussion prompt

Predict (int) 2.7 and (int) -2.7. Are they what you would get from rounding down? Where does the difference between 'toward zero' and 'down' show up?

Hint: Draw a number line and mark which way zero is from each value.

Answer:

(int) 2.7 is 2 and (int) -2.7 is -2. Rounding down would give 2 and -3, so the two descriptions differ for every negative value with a fractional part.

Toward zero means the magnitude always shrinks: 2.7 moves left to 2, and -2.7 moves right to -2. Both move toward the origin.

This matters when you split a negative quantity into units — a temperature below zero, a debt. It is also exactly the behaviour of integer division, which is not a coincidence: both are defined to truncate rather than to floor.

25. The remainder operator

Section

Section 3.8

26. Division gives the quotient; % gives the remainder

Concept

You have seen division, which computes the quotient of two numbers. Java also provides the modulo operation %, which divides two numbers and computes the remainder. Together they split a quantity into larger and smaller units.

feet = 76 / 12;      // quotient   -> 6
inches = 76 % 12;    // remainder  -> 4
// so 76 inches is 6 feet, 4 inches
expressionpronouncedvaluemeaning
76 / 1276 divided by 126how many whole feet
76 % 1276 mod 124how many inches left over
6 * 12 + 4—76the two together account for everything

The last row is the check worth doing every time: quotient times divisor, plus remainder, equals the original. If it does not, one of the two operators is wrong.

27. The two halves of one division

Picture it

/ and % are not two unrelated operators. They are the two answers to the same division, and you almost always want both.

Figure (svg): A diagram showing 76 inches split by division into 6 feet with a remainder of 4 inches

Notice that both steps divide by the same number. When you see that pattern in code — a / and a % with the same right-hand operand — it is almost always a quantity being split into units.

28. Extracting digits with the remainder operator

Worked example

Modular arithmetic turns out to be surprisingly useful. One of its neatest uses is pulling individual digits out of a number.

int x = 1234;
int ones = x % 10;        // 4
int lastTwo = x % 100;    // 34
int tens = (x / 10) % 10; // 3
expressionstepvalue
x % 10remainder after dividing by 104 — the rightmost digit
x % 100remainder after dividing by 10034 — the last two digits
x / 10integer division shifts right123
(x / 10) % 10then take the new rightmost digit3 — the tens digit

Use % 10 to get the rightmost digit.

Why: The remainder after dividing by ten is exactly what is left over below ten.

Use % 100 to get the last two.

Why: The same idea one place further along.

Use / 10 to discard the rightmost digit.

Why: Integer division by ten shifts the number right, throwing the last digit away.

Combine the two to reach any digit.

Why: Shift right until the one you want is rightmost, then take % 10.

Verify: Check that ones is 4, lastTwo is 34 and tens is 3.

Why: Then verify the identity: (x / 10) * 10 + (x % 10) should give back 1234 exactly. If it does, your understanding of both operators is consistent.

29. What is 17 % 5?

Prediction

Quotient and remainder.

System.out.println(17 / 5);
System.out.println(17 % 5);
expressionmeaningvalue
17 / 5how many whole 5s fit in 173
17 % 5what is left over2
3 * 5 + 2the check17

Predict first

What are the two values printed?

  • 3 then 2
  • 3 then 3
  • 3.4 then 2
  • 2 then 3

Correct: 3 then 2

Why: Three fives fit into seventeen with two left over, so the quotient is 3 and the remainder is 2. The check that always applies is quotient times divisor plus remainder: 3 x 5 + 2 = 17, which accounts for the whole original value.

30. What modular arithmetic is good for

Concept

Three uses worth knowing now, and one piece of terminology the book is careful about.

usehowexample
testing divisibilityif x % y is 0, then x is divisible by yn % 2 == 0 tests for even
extracting digitsx % 10 is the rightmost digit1234 % 10 is 4
splitting into unitsquotient and remainder together76 % 12 gives leftover inches
wrapping aroundkeeping a value in a range(hour + 5) % 12

On the name: many people and textbooks call % the modulus operator. In mathematics the modulus is the number you are dividing by — in 76 % 12 the modulus is 12. The Java language specification calls % the remainder operator, which is what it computes. It may help to think of the symbol as a division sign rotated to the left.

31. Reading % as a percent sign

Trap

The trap

The misreading. The symbol looks like a percent sign, so it must compute a percentage.

int score = 45;
int total = 60;
System.out.println(score % total);   // expected a percentage
what was expectedwhat % computesactual output
75, a percentagethe remainder of 45 divided by 6045
—45 divided by 60 is 0, remainder 45—

The output is 45 — the whole of score, because 45 does not contain a single 60. Nothing warns you; it is a logic error, and the number looks plausible enough to survive a glance.

The fix

% is the remainder operator. For a percentage, multiply and divide.

int score = 45;
int total = 60;
System.out.println(score * 100 / total);   // 75

// and % for what it is actually for:
System.out.println(score % total);         // 45, the remainder
expressioncomputes
score * 100 / totala percentage — multiply before dividing
score % totalthe remainder of the division
score / totalthe quotient — how many whole times it fits

The percentage line is Chapter 2's rule again: with integer division, multiply before you divide. Two chapters, one habit.

32. Match the expression to what it extracts

Matching

With x = 5678.

Match the pairs

  • a. x % 10
  • b. x % 100
  • c. x / 1000
  • d. x % 2
  • r1. 8 — the rightmost digit
  • r2. 78 — the last two digits
  • r3. 5 — the leftmost digit
  • r4. 0 — so x is even

Why: Remainder by a power of ten keeps the rightmost digits; integer division by a power of ten discards them and keeps what is to the left. Remainder by 2 is the standard divisibility test — a result of 0 means the number divides exactly, which for 2 means even.

33. Split seconds into minutes and seconds

Fill the middle

Given a total number of seconds, produce whole minutes and the leftover seconds.

Fill in the blanks

int total = 197;
int minutes = total / 60;
int seconds = total % 60;
// expect 3 minutes, 17 seconds

Why: Integer division gives the number of whole minutes (197 / 60 is 3) and the remainder gives the seconds left over (197 % 60 is 17). Both divide by the same number, which is the signature of a quantity being split into units — and the check 3 x 60 + 17 = 197 confirms nothing was lost.

34. Where else does wrapping around appear?

Real world

The remainder operator keeps a value inside a range. That is more useful than it sounds.

Discussion prompt

(hour + 5) % 12 moves a clock hand forward five hours and wraps past twelve. What other everyday quantities wrap around like this, and how would you compute them?

Hint: Think about anything cyclical — days, angles, positions in a list.

Answer:

Days of the week ((day + n) % 7), angles in degrees (angle % 360), and positions in a repeating list are the common ones. Anything cyclical is a remainder waiting to be written.

In programming specifically, index % length is how you step through an array and start again at the beginning — which you will use as soon as you meet arrays in Chapter 7. And many encryption algorithms use remainders extensively, because wrapping is what keeps values inside a fixed range.

35. Putting it all together

Section

Section 3.9

36. A complete program, from input to formatted output

Concept

At this point you have seen enough Java to write useful programs. You can import library classes, create a Scanner, get input from the keyboard, format output with printf, and divide and mod integers. Here is everything together.

import java.util.Scanner;

/**
 * Converts centimeters to feet and inches.
 */
public class Convert {

    public static void main(String[] args) {
        double cm;
        int feet, inches, remainder;
        final double CM_PER_INCH = 2.54;
        final int IN_PER_FOOT = 12;
        Scanner in = new Scanner(System.in);

        // prompt the user and get the value
        System.out.print("Exactly how many cm? ");
        cm = in.nextDouble();

        // convert and output the result
        inches = (int) (cm / CM_PER_INCH);
        feet = inches / IN_PER_FOOT;
        remainder = inches % IN_PER_FOOT;
        System.out.printf("%.2f cm = %d ft, %d in\n",
                          cm, feet, remainder);
    }
}
section of the programlinesjob
import1make Scanner available
documentation comment3-5say what the class is for
declarations and constants9-13every variable and constant, at the top
prompt and read16-17get the value from the user
convert and output20-24compute, then format

Read it once for shape rather than detail. Every line uses something from this chapter or the previous two — there is nothing new in it.

37. The layout choices, and why each was made

Notation

Think Java is explicit about the formatting decisions here, and each one has a stated reason. They are worth adopting as habits now.

Annotate

  • All variables and constants are declared at the top of main. Not required, but it makes their types easy to find later, and it tells the reader what data the algorithm involves before they read it.
  • Each major step is separated by a blank line and begins with a comment. The comments say what this step is for, not what each line does — the code already says that.
  • The constants are final and named in ALL_CAPS. 2.54 and 12 would both be magic numbers otherwise, and neither should ever change.
  • The division and the modulo divide by the same number. Many algorithms perform them together, and seeing them adjacent is the clue that a quantity is being split into units.
  • The printf is broken across two lines. When a statement gets longer than about 80 characters, a common convention is to split it — the reader should never have to scroll horizontally.
  • The class has a documentation comment starting /**, which is a different thing from //. Appendix B covers it, and it is what generates the API documentation you have been reading on Oracle's site.

None of this changes what the program does. All of it changes how long it takes the next person to understand — and the next person is usually you.

38. Tracing Convert with a real input

Worked example

Run the program by hand with 254 centimetres and check every intermediate value.

// with cm = 254.0
inches = (int) (cm / CM_PER_INCH);
feet = inches / IN_PER_FOOT;
remainder = inches % IN_PER_FOOT;
statementexpressionvalue
read cmin.nextDouble()254.0
inches(int) (254.0 / 2.54)(int) 100.0 = 100
feet100 / 128
remainder100 % 124
output%.2f cm = %d ft, %d in254.00 cm = 8 ft, 4 in

Divide by the conversion factor, in floating point.

Why: 254.0 / 2.54 is exactly 100.0 — the parentheses make sure this division happens before the cast.

Cast the result to an int.

Why: 100.0 becomes 100. Any fractional part of an inch is discarded here, which is the intended behaviour.

Split the inches into feet with integer division.

Why: 100 / 12 is 8 whole feet.

Take the remainder for the leftover inches.

Why: 100 % 12 is 4. Check: 8 x 12 + 4 = 100.

Verify: Run the program, enter 254, and expect 254.00 cm = 8 ft, 4 in.

Why: Then check the arithmetic independently: 8 feet 4 inches is 100 inches, and 100 x 2.54 is 254 cm exactly. Both directions agree, so the conversion is right.

39. Order the phases of the Convert program

Ranking

Five phases, one sensible order.

Put in order

  1. import the Scanner class
  2. declare variables and constants
  3. create the Scanner
  4. prompt the user and read the value
  5. convert and print the result

Why: The import must come first and outside the class. Inside main, everything must be declared before use, the Scanner must exist before you read from it, and you must have the value before you can convert it. This order is not arbitrary — each step depends on the one before.

40. Why declare everything at the top?

Concept

It is a stylistic choice, not a rule, and it is worth understanding the trade rather than following it blindly.

declare at the topdeclare where first used
all types visible in one placethe declaration is next to the use
tells the reader what data is involvedshorter distance between related lines
a variable may exist before it is meaningfuleach variable exists only where it matters
the book's choice for these programsmore common in modern professional Java

Think Java's reason is pedagogical and good: for a short program you are learning to read, seeing all the data at the top helps. In larger programs the second column tends to win, and Chapter 10's discussion of scope is where you will get the vocabulary to argue about it properly.

41. The same program, made unreadable

Error analysis

Every line here is correct and the program produces identical output. Read the annotations and count what has been lost.

Annotate

  • Single-letter names. c, f, i and r — you have to reconstruct what each holds from how it is used, every time you read the program.
  • The magic numbers are back. 2.54 and 12 appear inline with nothing to say what they are. A reader has to recognise the conversion factor to understand the line.
  • No blank lines and no comments. The three phases — set up, read, convert — run together into one block, so the structure has to be inferred instead of seen.
  • Several statements per line. A missing semicolon now produces an error pointing at a line with four statements on it, so the message narrows the search far less.
  • What was NOT lost: the output, exactly. This is the point — readability is not correctness, and the compiler is indifferent. The cost is paid entirely by the next person to open the file.

Compare this against the version on the previous slides. They are the same program; only one of them explains itself.

42. What each part of the program is for

Comparison

Fill the blanks from the program you have just traced.

Comparison matrix

linewhat it produceswhich chapter it came from
cm = in.nextDouble();a double read from the keyboardChapter 3, the Scanner
inches = (int) (cm / CM_PER_INCH);whole inches, fraction discardedChapter 3, casting
remainder = inches % IN_PER_FOOT;inches left over after whole feetChapter 3, the remainder operator
final double CM_PER_INCH = 2.54;a named constant that cannot be reassignedChapter 3, constants

Every line of the program is something you have been taught. That is what 'putting it all together' means, and it is a good moment to notice how much you can now write.

43. Why is the cast parenthesised?

Explain it to yourself

One pair of parentheses in the program is load-bearing.

Discussion prompt

In inches = (int) (cm / CM_PER_INCH); the second pair of parentheses is essential. Explain what would happen without them, and why the answer would be wrong rather than merely different.

Hint: A cast grabs the smallest thing to its right.

Answer:

Without them, the cast applies to cm alone: (int) cm / CM_PER_INCH truncates the centimetres first, then divides. For cm = 254.0 the truncation changes nothing, but for cm = 254.9 you would divide 254 rather than 254.9 — losing precision before the calculation instead of after it.

Worse, the result of int / double is a double, so assigning it to an int would be a compile error. So the mistake either fails loudly or silently loses precision, depending on the values — which is the worst kind of bug, because testing with round numbers hides it.

44. Sketch the program before writing it

Blank canvas

Before the next problem set, practise the step that comes before typing.

Draw it

On paper, sketch a program that reads a number of seconds and reports it as hours, minutes and seconds. Do not write Java. Write: what variables you need and their types, which constants you would name, and which three lines will use / and % together. Then check your sketch against the structure of Convert — it should have the same five phases.

45. The Scanner bug

Section

Section 3.10

46. Reading an int then a line goes wrong

Concept

Now that you have some experience with Scanner, a warning about an unexpected behaviour. Reading a String followed by an int works fine. Reading an int followed by a String does something strange.

System.out.print("What is your age? ");
age = in.nextInt();
System.out.print("What is your name? ");
name = in.nextLine();
System.out.printf("Hello %s, age %d\n", name, age);
stepwhat you expectwhat happens
print the age promptWhat is your age? the same
nextIntreads 45reads 45
print the name promptWhat is your name? the same
nextLinewaits for you to typereturns immediately with an empty string
outputHello Grace Hopper, age 45Hello , age 45

The program does not let you input your name at all. It displays What is your name? Hello , age 45 and finishes. Nothing crashes, and there is no error message — this is a logic error caused by a detail of how Scanner works.

47. Scanner sees a stream, not lines

Picture it

To understand what is happening, you need to realise that Scanner does not see input as multiple lines the way you do. It gets a stream of characters, with a position marking what comes next.

Figure (svg): A character stream showing 4, 5, newline, then Grace, with an arrow marking the position after nextInt has run

nextInt reads characters until it gets to a non-digit — so it consumes the 4 and the 5 and stops. The newline you pressed is still sitting there, unread. Then nextLine reads characters until it gets to a newline, finds one immediately, and returns the empty string.

48. Fixing it with an extra nextLine

Worked example

Once you can see the leftover newline, the fix is obvious: consume it before reading the line you actually want.

System.out.print("What is your age? ");
age = in.nextInt();
in.nextLine();                      // read the rest of the line
System.out.print("What is your name? ");
name = in.nextLine();
System.out.printf("Hello %s, age %d\n", name, age);
callreadsleaves the position
nextInt()45just before the newline
nextLine() <- the fixthe rest of the line: nothing but the newlineat the start of the next line
print the prompt—unchanged
nextLine()Grace Hopperat the start of the line after

Notice what nextInt leaves behind.

Why: It stops at the first non-digit, so the newline you typed is still in the stream.

Add a bare in.nextLine(); immediately after the nextInt.

Why: It reads the rest of that line, which is just the newline character, and discards it.

Note that its return value is ignored.

Why: The call is made purely for its effect on the stream position. That is unusual and worth a comment.

Then read the name as normal.

Why: Now the position is at the start of a fresh line, so nextLine gets what the user types.

Verify: Run it, enter 45 and then Grace Hopper, and expect Hello Grace Hopper, age 45.

Why: If the name still comes out empty, check the extra nextLine is AFTER the nextInt and BEFORE the prompt — its position in the sequence is the whole fix.

This technique is common when reading int or double values that appear on their own line: first read the number, then read the rest of the line.

49. What does name contain?

Prediction

The user types 45, then Enter, then Grace Hopper.

age = in.nextInt();
name = in.nextLine();
callreadsreturns
nextInt()the characters 4 and 545
nextLine()up to the next newline — which is immediatethe empty string ""

Predict first

What does name hold after these two lines?

  • the empty string ""
  • "Grace Hopper"
  • "45"
  • the program waits for input

Correct: the empty string ""

Why: nextInt stops as soon as it reaches a non-digit, leaving the newline unread. nextLine then reads characters until it reaches a newline — and the very next character is one — so it returns immediately with nothing. The user never gets a chance to type the name.

50. Why the other order works fine

Concept

Reading a String and then an int causes no trouble at all. Understanding why confirms that the explanation above is right rather than a rule to memorise.

orderfirst callleavessecond callresult
String then intnextLine reads the whole line INCLUDING its newlineat the start of the next linenextInt reads the digitsworks
int then StringnextInt reads the digits onlybefore the newlinenextLine finds a newline immediatelyempty string

The asymmetry is entirely in what each method consumes. nextLine swallows the newline that ends the line; nextInt does not. Every Scanner surprise you meet comes down to that one difference.

51. Adding the extra nextLine in the wrong place

Trap

The trap

Right idea, wrong position. The extra read is placed after the prompt.

age = in.nextInt();
System.out.print("What is your name? ");
in.nextLine();               // too late
name = in.nextLine();
callreadseffect
nextInt()45newline still unread
print the prompt——
nextLine()the leftover newlinecorrect so far
nextLine()waits — the user types the nameworks, but the prompt already scrolled past

This one actually works, which makes it a poor example of failure and a good example of confusion: the user is prompted, then nothing appears to happen for a moment. Put the fix where it belongs.

The fix

Immediately after the nextInt, with a comment saying why.

age = in.nextInt();
in.nextLine();               // read the newline left by nextInt
System.out.print("What is your name? ");
name = in.nextLine();
callpurpose
nextInt()get the number
nextLine()discard the rest of that line — the fix
printprompt for the next thing
nextLine()get the name

The comment is not optional. A call whose return value is thrown away looks like a mistake to the next reader, so the line has to say what it is for.

52. Which sequences need the extra nextLine?

Discrimination

The rule follows from what each method consumes.

Sort into buckets

Sort each pair of consecutive Scanner calls.

needs an extra nextLine
nextInt() then nextLine(); nextDouble() then nextLine()
works as written
nextLine() then nextInt(); nextLine() then nextLine()
need
The first call reads a number and stops at the newline without consuming it, so the following nextLine finds that newline immediately and returns an empty string.
fine
Either the first call is a nextLine, which consumes the newline that ends its line, or the second call is a number-reader, which skips over whitespace including newlines before it starts.

53. Annotate the stream

Error analysis

The stream of characters the Scanner sees, for the input 45 Enter Grace Hopper Enter.

Annotate

  • nextInt read the 4 and the 5 and stopped at the first non-digit, which is the newline. It did not consume it.
  • The position now sits on the newline. From the Scanner's point of view there are no 'lines' — only a sequence of characters and a place in it.
  • nextLine reads until it reaches a newline. The very next character is one, so it reads nothing and returns the empty string, moving the position past it.
  • A second nextLine would then read Grace Hopper — which is exactly why the fix is to call nextLine twice: once to finish the number's line, once to get the text.
  • The general lesson: when a library behaves strangely, look for the model it is really using. Scanner's model is a character stream, and every surprise follows from that rather than from a bug.

Being able to draw this stream is what turns the Scanner bug from a rule you memorise into a behaviour you can predict.

54. Could Scanner have been designed differently?

Counterexample

The behaviour is surprising. Ask whether it is wrong.

Discussion prompt

Would it be better if nextInt consumed the rest of the line, so this bug could not happen? What would that break?

Hint: Think about reading several numbers from one line.

Answer:

It would break reading several values from a single line. 3 4 5 on one line is read by three nextInt calls precisely because nextInt stops at the first non-digit and leaves everything after it available.

So the design is a genuine trade rather than an oversight: nextInt reads a number, not a line containing a number, and that is the more general behaviour. The cost is that mixing it with nextLine requires you to know what each one consumes.

This is worth generalising. When a library surprises you, it is usually optimising for a case you are not currently in. Finding that case is often faster than memorising the workaround.

55. The tools this chapter added

Comparison

Five new pieces of machinery. Fill the blanks.

Comparison matrix

toolwhat it doesthe trap
printfformats output with specifiersvalues are separated by commas, not joined with +
(int) castconverts a double to an int, discarding the fractionit binds tighter than * and /
%computes the remainder of a divisionit is not a percent sign
finalforbids reassignment after initialisationname it ALL_CAPS so readers know
Scannerreads numbers and lines from a streamnextInt leaves the newline behind

Four of the five traps are run-time or logic errors — the compiler catches none of them. That is what makes predicting your output before running it worth the effort.

56. The pattern to carry away

Pattern

Splitting a quantity into units is the shape you will reuse most from this lesson, and it is always the same three lines.

int totalSmall = 197;              // seconds, inches, pence...
final int PER_BIG = 60;           // ...per minute, foot, pound

int big   = totalSmall / PER_BIG; // 3
int small = totalSmall % PER_BIG; // 17
// check: big * PER_BIG + small == totalSmall
stepoperatorgives
how many whole big units/the quotient
how much is left over%the remainder
the checkbig * PER_BIG + smallthe original total

57. Check: where the cast applies

Check

Work it out before you click.

double d = 7.8;
System.out.println((int) d * 10);
stepexpressionvalue
cast binds first(int) 7.87
then multiply7 * 1070
compare(int) (7.8 * 10)78

Check your understanding

What does this display?

  • A. 70 (correct)
  • B. 78
  • C. 80
  • D. 78.0

Answer: A

Why: Casting takes precedence over arithmetic, so (int) d is evaluated first and truncates 7.8 to 7; multiplying by 10 then gives 70. To get 78 you would write (int) (d * 10), forcing the multiplication to happen before the cast.

Why B tempts people
This assumes the cast applies to the whole expression, but a cast binds to the smallest thing on its right unless parentheses say otherwise.
Why C tempts people
This rounds 7.8 up to 8 before multiplying, but casting truncates toward zero rather than rounding to nearest.
Why D tempts people
The cast produces an int, and an int times an int is an int, so no decimal point appears.

58. Check: quotient and remainder

Check

Work it out before you click.

int total = 100;
int feet = total / 12;
int inches = total % 12;
expressionvalue
100 / 128
100 % 124
8 * 12 + 4100

Check your understanding

What are feet and inches?

  • A. feet = 8, inches = 4 (correct)
  • B. feet = 8, inches = 8
  • C. feet = 12, inches = 4
  • D. feet = 8.33, inches = 4

Answer: A

Why: Eight whole twelves fit into 100 with 4 left over, so integer division gives 8 feet and the remainder gives 4 inches. The check confirms it: 8 x 12 + 4 is exactly 100, so nothing has been lost or double-counted.

Why B tempts people
This uses the quotient for both, but % computes what is left over rather than repeating the quotient.
Why C tempts people
This swaps the roles — 12 is the divisor, the number of inches per foot, not the answer.
Why D tempts people
Both operands are ints, so the division is integer division and produces 8 rather than 8.33.

59. Check: the Scanner bug

Check

Work it out before you click.

System.out.print("Age? ");
int age = in.nextInt();
System.out.print("Name? ");
String name = in.nextLine();
callleaves the position
nextInt()just before the newline
nextLine()reads to the newline — which is immediate
resultname is empty

Check your understanding

What is the fix?

  • A. Add a bare in.nextLine(); immediately after the nextInt (correct)
  • B. Use nextInt() to read the name as well
  • C. Move the prompt before the nextInt
  • D. Create a second Scanner for the name

Answer: A

Why: nextInt stops at the first non-digit and leaves the newline in the stream, so an extra nextLine is needed to consume the rest of that line before the real read. Placing it immediately after the nextInt, with a comment, makes the intent clear.

Why B tempts people
nextInt reads an integer and would throw InputMismatchException when handed a name, since a name is not a number.
Why C tempts people
The prompt's position affects only what the user sees, not what is left in the input stream.
Why D tempts people
A second Scanner reads from the same System.in stream and inherits exactly the same leftover newline, so it fixes nothing.

60. Truncation, and who notices

Real world

This lesson gave you two ways to discard information on purpose: casting and integer division. Both are correct and both cause real bugs when used without thinking.

Discussion prompt

A shop's till computes a discount as (int) (price * 0.15) and subtracts it. Who benefits from the truncation, by how much, and how often would anyone notice?

Hint: Work out the discount on a price of 9.99.

Answer:

The shop benefits. 15% of 9.99 is 1.4985, and the cast makes it 1 — the customer loses about 50p on that transaction, every time.

Nobody notices, because each individual case is small and plausible. That is the signature of a truncation bug: it is never wildly wrong, so it survives testing and casual inspection, and it is only visible in aggregate.

The habit worth taking: every time you write a cast or divide two integers, say out loud what happens to the part being discarded and whether that is what you want. Sometimes truncating is exactly right — whole feet, whole pages. The bug is not the truncation; it is not having decided.

61. How sure are you?

Commit first

Commit to an answer and to your confidence.

Predict first

What does (int) -7.9 evaluate to?

  • -7
  • -8
  • 7
  • -7.9

Correct: -7

Why: Casting to an integer always rounds toward zero, which for a negative value means moving up rather than down. So -7.9 becomes -7, not -8. If you answered -8 you were applying 'round down', which agrees with 'toward zero' for positive numbers and disagrees for every negative one — which is exactly why Think Java words the rule as 'toward zero'.

62. Explain it to someone else

Explain it

Two minutes, out loud, drawing as you go.

Discussion prompt

Explain the Scanner bug to someone who has just hit it. You must draw the stream of characters and show where the position is after nextInt runs. Then say what the fix is and why it goes where it goes.

Hint: Draw the newline as a visible character. That is the whole explanation.

Answer:

Scanner does not see lines — it sees one long stream of characters with a marker showing what comes next. When you type 45 and press Enter, the stream holds 4, 5 and a newline. nextInt reads digits and stops as soon as it hits something that is not a digit, so it takes the 4 and the 5 and leaves the marker sitting on the newline. Then nextLine reads up to the next newline — and it is right there — so it returns an empty string without waiting.

The fix is an extra nextLine straight after the nextInt, to eat that leftover newline. It goes there rather than later because you want the stream tidy before you prompt for anything else.

If your explanation did not include the drawing, try again with it. This is a case where the picture is the explanation, and prose alone tends to produce a rule people memorise and misapply.

63. Exit ticket

Exit ticket

One question before you close the deck.

Predict first

Why does (int) (cm / CM_PER_INCH) need its inner parentheses?

  • Because a cast binds to the smallest thing on its right, so without them it would truncate cm before dividing
  • Because Java requires parentheses after every cast
  • Because the division would otherwise be integer division
  • Because CM_PER_INCH is final

Correct: Because a cast binds to the smallest thing on its right, so without them it would truncate cm before dividing

Why: Type casting takes precedence over arithmetic, so (int) cm / CM_PER_INCH would convert cm to an int first and then divide — losing the fractional centimetres before the calculation and producing a double that cannot be assigned to an int. The parentheses require the division to happen first, so the cast applies to the finished result. Parentheses are not required after a cast in general; they are required here because of what the cast would otherwise grab.

64. Draw the whole lesson

Connect it up

One page, from memory.

Draw it

Draw the character stream for the input 45 Enter Grace Hopper Enter, marking the position after nextInt and after each subsequent call, and label where the empty string comes from. Beside it, write the three-line unit-splitting pattern with / and %, and the check that confirms it. Finally, write out (int) p * 2 and (int) (p * 2) for p = 3.9, with their values and one sentence on why they differ.

65. Recap

Recap

Five sections that complete the toolkit for a program that reads input, computes, and reports a result in the units a person actually wants.

if you remember one thingit is this
about errorsfirst line for what, last line for where
about castsit grabs the smallest thing to its right
about Scannerit reads a stream, not lines

Sources

  1. Downey & Mayfield, Think Java, 2nd edition (Green Tea Press / O'Reilly, 2020) — Think Java 2e, Chapter 3 (Input and Output), Sections 3.6-3.10, pp. 40-46
  2. The Java Tutorials — Assignment, Arithmetic, and Unary Operators
  3. Think Java 2e — free online edition and source code

Want this taught 1-on-1? Alexander tutors Java — $55/session, free consultation.

Book on Wyzant · Text (657) 465-8108