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
Title
Think Java 2e · Chapter 3 · Input and Output
Sections 3.6-3.10 · pp. 40-46
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.
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.
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.
Section
Section 3.6
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| step | what happens |
|---|---|
| concatenation happens first | if inch is 100, the result is "inches = %d100" |
| printf receives that one string | a format string, and no values to format |
| printf reaches %d | there is no value for it |
| result | MissingFormatArgumentException 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.
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
MissingFormatArgumentException, followed by the detail: Format specifier '%d'. That means it does not know what value to substitute for %d.Example.main(Example.java:10) — the method you actually wrote, the file it is in, and the line number.+ 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.
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);| step | what you learn | from where |
|---|---|---|
| what happened | MissingFormatArgumentException, specifier %d | the first line |
| where | Example.main, Example.java line 10 | the last line |
| what is on that line | a printf with a + in it | reading your own code |
| the cause | concatenation ran before printf, so no values were passed | reasoning |
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.
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);| line | arguments to the call | result |
|---|---|---|
| println("x = " + x) | one string | x = 5 |
| printf("x = %d%n", x) | format string + one value | x = 5 |
| printf("x = %d%n" + x) | one string, no values | throws |
Predict first
Which call throws an exception at run time?
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.
Concept
The confusion is understandable, because println really does want a +. The two methods take their arguments in completely different ways.
| method | how many arguments | how values are joined |
|---|---|---|
| println | exactly one | you build it yourself with + |
| printf | a format string, then one per specifier | printf 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.
Trap
The habit from println, carried over to printf where it does not belong.
System.out.printf("inches = %d" + inch);| printf receives | specifiers in it | values 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.
A comma, so the value arrives as a separate argument.
System.out.printf("inches = %d%n", inch);| printf receives | specifiers | values | result |
|---|---|---|---|
| "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.
Notation
A different exception, the same structure.
Annotate
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.
Definition probe
Chapter 2's three-way classification, applied to this lesson's mistakes.
Sort into buckets
Sort each mistake.
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.
Section
Section 3.7
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| conversion | automatic? | why |
|---|---|---|
| int to double | yes | nothing is lost — 3 becomes 3.0 |
| double to int | no | the fractional part would be discarded |
| (int) pi | explicit cast | you 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.
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.
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| step | expression | value |
|---|---|---|
| as written | (int) pi * 20.0 | — |
| the cast binds first | 3 * 20.0 | pi became the int 3 |
| then the multiplication | 60.0 | an 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.
Prediction
Casting rounds toward zero.
double d = 9.99;
int x = (int) d;| value | operation | result |
|---|---|---|
| 9.99 | (int) — discard the fraction | 9 |
| — | not rounding to nearest | not 10 |
Predict first
What value does x hold?
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.
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);| step | why |
|---|---|
| cm / CM_PER_INCH | floating-point division — CM_PER_INCH is a double |
| the parentheses | force the division to happen before the cast |
| (int) | converts the result, rounding toward zero |
| %d for inch | inch 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.
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| step | expression | type |
|---|---|---|
| the cast binds first | (int) cm | int — 254 |
| then the division | 254 / 2.54 | double — 100.0 |
| assign to an int | int 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.
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| step | expression | type |
|---|---|---|
| the parentheses | cm / CM_PER_INCH | double — 100.0 |
| then the cast | (int) 100.0 | int — 100 |
| assign | int 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.
Prediction
A cast takes precedence over arithmetic.
double p = 3.9;
System.out.println((int) p * 2);
System.out.println((int) (p * 2));| expression | order | result |
|---|---|---|
| (int) p * 2 | cast first: 3, then 3 * 2 | 6 |
| (int) (p * 2) | multiply first: 7.8, then cast | 7 |
Predict first
What two values are printed?
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.
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.
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.
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.
Section
Section 3.8
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| expression | pronounced | value | meaning |
|---|---|---|---|
| 76 / 12 | 76 divided by 12 | 6 | how many whole feet |
| 76 % 12 | 76 mod 12 | 4 | how many inches left over |
| 6 * 12 + 4 | — | 76 | the 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.
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.
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| expression | step | value |
|---|---|---|
| x % 10 | remainder after dividing by 10 | 4 — the rightmost digit |
| x % 100 | remainder after dividing by 100 | 34 — the last two digits |
| x / 10 | integer division shifts right | 123 |
| (x / 10) % 10 | then take the new rightmost digit | 3 — 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.
Prediction
Quotient and remainder.
System.out.println(17 / 5);
System.out.println(17 % 5);| expression | meaning | value |
|---|---|---|
| 17 / 5 | how many whole 5s fit in 17 | 3 |
| 17 % 5 | what is left over | 2 |
| 3 * 5 + 2 | the check | 17 |
Predict first
What are the two values printed?
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.
Concept
Three uses worth knowing now, and one piece of terminology the book is careful about.
| use | how | example |
|---|---|---|
| testing divisibility | if x % y is 0, then x is divisible by y | n % 2 == 0 tests for even |
| extracting digits | x % 10 is the rightmost digit | 1234 % 10 is 4 |
| splitting into units | quotient and remainder together | 76 % 12 gives leftover inches |
| wrapping around | keeping 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.
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 expected | what % computes | actual output |
|---|---|---|
| 75, a percentage | the remainder of 45 divided by 60 | 45 |
| — | 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.
% 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| expression | computes |
|---|---|
| score * 100 / total | a percentage — multiply before dividing |
| score % total | the remainder of the division |
| score / total | the 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.
Matching
With x = 5678.
Match the pairs
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.
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.
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.
Section
Section 3.9
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 program | lines | job |
|---|---|---|
| import | 1 | make Scanner available |
| documentation comment | 3-5 | say what the class is for |
| declarations and constants | 9-13 | every variable and constant, at the top |
| prompt and read | 16-17 | get the value from the user |
| convert and output | 20-24 | compute, 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.
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
final and named in ALL_CAPS. 2.54 and 12 would both be magic numbers otherwise, and neither should ever change./**, 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.
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;| statement | expression | value |
|---|---|---|
| read cm | in.nextDouble() | 254.0 |
| inches | (int) (254.0 / 2.54) | (int) 100.0 = 100 |
| feet | 100 / 12 | 8 |
| remainder | 100 % 12 | 4 |
| output | %.2f cm = %d ft, %d in | 254.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.
Ranking
Five phases, one sensible order.
Put in order
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.
Concept
It is a stylistic choice, not a rule, and it is worth understanding the trade rather than following it blindly.
| declare at the top | declare where first used |
|---|---|
| all types visible in one place | the declaration is next to the use |
| tells the reader what data is involved | shorter distance between related lines |
| a variable may exist before it is meaningful | each variable exists only where it matters |
| the book's choice for these programs | more 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.
Error analysis
Every line here is correct and the program produces identical output. Read the annotations and count what has been lost.
Annotate
c, f, i and r — you have to reconstruct what each holds from how it is used, every time you read the program.Compare this against the version on the previous slides. They are the same program; only one of them explains itself.
Comparison
Fill the blanks from the program you have just traced.
Comparison matrix
| line | what it produces | which chapter it came from |
|---|---|---|
| cm = in.nextDouble(); | a double read from the keyboard | Chapter 3, the Scanner |
| inches = (int) (cm / CM_PER_INCH); | whole inches, fraction discarded | Chapter 3, casting |
| remainder = inches % IN_PER_FOOT; | inches left over after whole feet | Chapter 3, the remainder operator |
| final double CM_PER_INCH = 2.54; | a named constant that cannot be reassigned | Chapter 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.
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.
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.
Section
Section 3.10
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);| step | what you expect | what happens |
|---|---|---|
| print the age prompt | What is your age? | the same |
| nextInt | reads 45 | reads 45 |
| print the name prompt | What is your name? | the same |
| nextLine | waits for you to type | returns immediately with an empty string |
| output | Hello Grace Hopper, age 45 | Hello , 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.
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.
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);| call | reads | leaves the position |
|---|---|---|
| nextInt() | 45 | just before the newline |
| nextLine() <- the fix | the rest of the line: nothing but the newline | at the start of the next line |
| print the prompt | — | unchanged |
| nextLine() | Grace Hopper | at 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.
Prediction
The user types 45, then Enter, then Grace Hopper.
age = in.nextInt();
name = in.nextLine();| call | reads | returns |
|---|---|---|
| nextInt() | the characters 4 and 5 | 45 |
| nextLine() | up to the next newline — which is immediate | the empty string "" |
Predict first
What does name hold after these two lines?
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.
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.
| order | first call | leaves | second call | result |
|---|---|---|---|---|
| String then int | nextLine reads the whole line INCLUDING its newline | at the start of the next line | nextInt reads the digits | works |
| int then String | nextInt reads the digits only | before the newline | nextLine finds a newline immediately | empty 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.
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();| call | reads | effect |
|---|---|---|
| nextInt() | 45 | newline still unread |
| print the prompt | — | — |
| nextLine() | the leftover newline | correct so far |
| nextLine() | waits — the user types the name | works, 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.
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();| call | purpose |
|---|---|
| nextInt() | get the number |
| nextLine() | discard the rest of that line — the fix |
| prompt 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.
Discrimination
The rule follows from what each method consumes.
Sort into buckets
Sort each pair of consecutive Scanner calls.
Error analysis
The stream of characters the Scanner sees, for the input 45 Enter Grace Hopper Enter.
Annotate
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.Being able to draw this stream is what turns the Scanner bug from a rule you memorise into a behaviour you can predict.
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.
Comparison
Five new pieces of machinery. Fill the blanks.
Comparison matrix
| tool | what it does | the trap |
|---|---|---|
| printf | formats output with specifiers | values are separated by commas, not joined with + |
| (int) cast | converts a double to an int, discarding the fraction | it binds tighter than * and / |
| % | computes the remainder of a division | it is not a percent sign |
| final | forbids reassignment after initialisation | name it ALL_CAPS so readers know |
| Scanner | reads numbers and lines from a stream | nextInt 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.
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| step | operator | gives |
|---|---|---|
| how many whole big units | / | the quotient |
| how much is left over | % | the remainder |
| the check | big * PER_BIG + small | the original total |
/ and % with the same divisor means a quantity is being split into units.Check
Work it out before you click.
double d = 7.8;
System.out.println((int) d * 10);| step | expression | value |
|---|---|---|
| cast binds first | (int) 7.8 | 7 |
| then multiply | 7 * 10 | 70 |
| compare | (int) (7.8 * 10) | 78 |
Check your understanding
What does this display?
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.
Check
Work it out before you click.
int total = 100;
int feet = total / 12;
int inches = total % 12;| expression | value |
|---|---|
| 100 / 12 | 8 |
| 100 % 12 | 4 |
| 8 * 12 + 4 | 100 |
Check your understanding
What are feet and inches?
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.
% computes what is left over rather than repeating the quotient.Check
Work it out before you click.
System.out.print("Age? ");
int age = in.nextInt();
System.out.print("Name? ");
String name = in.nextLine();| call | leaves the position |
|---|---|
| nextInt() | just before the newline |
| nextLine() | reads to the newline — which is immediate |
| result | name is empty |
Check your understanding
What is the fix?
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.
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.
Commit first
Commit to an answer and to your confidence.
Predict first
What does (int) -7.9 evaluate to?
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'.
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.
Exit ticket
One question before you close the deck.
Predict first
Why does (int) (cm / CM_PER_INCH) need its inner parentheses?
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.
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.
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 thing | it is this |
|---|---|
| about errors | first line for what, last line for where |
| about casts | it grabs the smallest thing to its right |
| about Scanner | it reads a stream, not lines |
+.% is the remainder operator, not a percent sign; / and % together split a quantity into units.Want this taught 1-on-1? Alexander tutors Java — $55/session, free consultation.