Phase 0 · Foundations
Every value has a size, and sizes run out
By the end you'll know what a variable really is, why Java insists on knowing what kind of thing goes inside it, and how to break a program with one + 1.
Think about the little counter on a bike, the one that counts how far you've ridden. It has six windows with digits in them. Ride far enough and it shows 999999. Ride one metre more and it doesn't show 1000000 — it can't. There's no seventh window. It flips back to 000000 and starts again. Your computer is made of counters exactly like that one. Bigger, but still with a fixed number of windows. In about twenty minutes we're going to ride one off the end.
What you'll be able to do
- Make a variable — a named place to keep a value — and change what's in it.
- Pick the right kind of variable: a score, a view count, a height, a yes/no, or a letter.
- Explain, out loud, why
0.1 + 0.2doesn't give0.3. - Predict what
7 / 2gives in Java — and be right, which most beginners are not. - Spot the two moments where a computer's counters run out, and know how to avoid them.
Section 1A variable is a labelled box
Picture the shoe lockers at a swimming pool. Each one is the same size. Each has a strip of tape on the door where you write a name. You put something in, shut the door, and later you come back, read the names, find yours, and take out whatever you left.
Memory is the computer's scratch space — where it keeps things it's using right now. It is a very long row of those lockers. Making a variable does three things. Java picks an unused locker, writes your chosen name on the tape, then puts a value inside. From here on this course calls one of them a box.
The name matters. Memory's boxes are told apart by number, and box 140732920755612 isn't something you want to type. So you give it a name instead, and Java remembers which number that name means.
Here's the whole thing in one line of Java. The lines you write are called code.
int score = 7;Read it left to right. int says what kind of thing this box will hold — a whole number, no fractions. score is the name going on the tape. The = means "put the thing on the right into the box on the left". 7 is the thing being put in. The ; at the end is a full stop. Java wants one after every instruction.
That's it. That's a variable. It's called a variable because the value inside can vary — you can swap it for a different one later. The name on the tape stays the same.
Section 2Declaring, assigning, and doing both at once
Those three jobs from Section 1 have names, and it's worth pulling them apart. Declaring is making the box and writing the name. Assigning is putting a value in. You can do them on separate lines.
int score; // declare: make the box, label it "score"
score = 7; // assign: put 7 in the box
System.out.println(score);
score = 12; // re-assign: throw away 7, put 12 in
System.out.println(score);
score = score + 1; // read the box, add 1, put the answer back
System.out.println(score);Line by line. Line 1 makes an empty box named score. The word int is still needed, because Java has to know how big to make it. Line 2 puts 7 inside. Line 3 prints what's inside. System.out.println is Java's "write this on the screen" instruction, which you met in 0.1.
Next comes score = 12;, which is a re-assignment. It does not make a second box. It opens the existing one, tips 7 out, and puts 12 in. The 7 is gone for good. Then it prints again. The last pair of lines is the odd bit, and we'll take it apart in a second.
Memory
int score; — the box exists and has a name. Nothing useful is in it yet. Java won't let you read it in this state. We'll prove that later.
Memory
score = 7; — 7 goes in. Now reading score anywhere in your program gives you 7.
Memory
score = 12; — same box, new contents. Still one box. The 7 no longer exists anywhere.
Memory
score = score + 1; — Java reads the box (12) and works out 12 + 1. Then it puts 13 back in the same box. Right side first, always.
Section 3= does not mean equals
This is the single biggest trip-up in your first week, so we're going to sit with it.
In maths class, = is a claim about the world. "x = 5" says x and 5 are the same thing. It's a fact, and you can read it in either direction.
In Java, = is an order. Work out whatever is on the right, then put the result in the box named on the left. It only goes one way. It claims nothing. Some people read it aloud as "gets" — score gets 7 — and honestly that's a better name for it.
Once you see = as an order and not a claim, score = score + 1 stops being nonsense. As maths it's impossible — no number equals itself plus one. As an order it makes plain sense: take what's in the box, add one, put it back.
Java does have a "really equals" for asking questions. It's two equals signs, ==, and it arrives next chapter. One = puts things in boxes. Two == asks a question.
Run through Boxes.java above in your head. It prints three times. What three numbers come out, and in what order? Guessing wrong here is genuinely useful — it tells you exactly which line you're reading as maths.
7
12
13The third one is the test. If you guessed 13, you're already reading = as an order. If you expected an error, or 12, that's the maths habit talking. Now you've felt the difference, which beats being told about it.
Section 4Why Java makes you say the kind first
A recycling bin has a shaped hole in the lid. The bottle bin has a round hole. The paper bin has a slot. You can't post a bottle through the slot, and nobody has to check. The shape does the checking, before anything goes wrong.
Java boxes have shaped holes. When you write int, you cut a whole-number-shaped hole in that box, and nothing else fits. This is called static typing.
Both halves of that phrase need unpacking, so here they are. "Typing" means kind of thing — what sort of value is allowed in here. And static means decided beforehand. It's settled before the program runs, and it can't change while it's running. So static typing means Java knows the kind of every box in advance, and checks it in advance.
Going deeper The other static — the one you already typed in 0.1
You typed the word static back in 0.1, inside public static void main. Annoyingly, it means something different there.
In "static typing" it's the general idea: fixed before the program runs. In static void main it's a Java keyword doing a specific job. It marks a method as belonging to the class itself, not to any one object.
The two do share a flavour. One fixed thing, not one per object, and not decided later. Without static on main, Java couldn't start your program, because there'd be no object to start it on. The full story is Phase 1, Chapter 1.2, once objects exist to compare it against.
Remember from 0.1 that Java has two stages. First javac compiles your code — checks it, then turns it into a form the computer can run. Then java runs the result. The type checking happens in stage one. So Java refuses to build a program it can already see is wrong.
A file in your src/ folder proves it. It's meant to fail.
int age = 15.5; // a whole-number box, given a fractionOne line. A box shaped for whole numbers, handed something with a half in it. One word in the coming error is worth meeting before you read it. Java calls the change lossy, which means it would lose part of the value — here, the .5. Here's exactly what javac says, run on this machine:
WrongKind.java:5: error: incompatible types: possible lossy conversion from double to int
int age = 15.5; // a whole-number box, given a fraction
^
1 errorUnpack that message, because you'll see its relatives for years. WrongKind.java:5 is the file and the line number. "incompatible types" means the shapes don't match. "lossy conversion" is the word we just met — changing 15.5 into a whole number would throw the .5 away. The ^ points at the exact spot.
Java is also strict about empty boxes. Try this one:
Empty.java:6: error: variable score might not have been initialized
System.out.println(score); // Java refuses to read it
^
1 error"Initialized" is a long word for "given its first value". The box was made but never filled. Java won't hand you whatever old rubbish is sitting in that patch of memory. Some languages would. Java won't.
Other languages let you skip all this and sort it out while the program runs. That feels faster to write. The trade is when you find out you were wrong. At build time, on your machine, with a line number. Or at run time, on someone else's phone.
Section 5The five kinds you'll actually use
Java has eight basic kinds, called primitive types. "Primitive" here means raw material — not built out of anything smaller. You'll use five constantly, and three almost never.
int score = 7;
long viewCount = 9000000000L; // the L says "this is a long"
double height = 1.62;
boolean gameOver = false;
char grade = 'B'; // single quotes, exactly one letterLine 1: int is your default for counting things — whole numbers, positive or negative. Line 2: long is an int with a much bigger box. The L on the end isn't optional. Without it Java reads 9000000000 as an int and complains, because it doesn't fit. Line 3: double holds numbers with a fractional part. Line 4: boolean holds one of exactly two values, true or false. Line 5: char holds a single character in single quotes. Double quotes mean something else, and that's a later chapter.
Running it prints each one back:
score 7
viewCount 9000000000
height 1.62
gameOver false
grade BNow the sizes. A bit is one on/off switch — the smallest thing a computer can store. Eight bits make one byte. These numbers came from running src/Sizes.java, which asks Java itself rather than trusting anyone's memory:
| Type | Bits | Holds | Smallest → biggest |
|---|---|---|---|
int | 32 | whole numbers | -2147483648 → 2147483647 |
long | 64 | bigger whole numbers | -9223372036854775808 → 9223372036854775807 |
double | 64 | fractions, roughly | up to 1.7976931348623157E308 |
boolean | — | true / false | two values, that's all |
char | 16 | one character | 0 → 65535 |
byte | 8 | tiny whole numbers | -128 → 127 |
short | 16 | small whole numbers | -32768 → 32767 |
float | 32 | fractions, less exactly | rarely worth it |
The bottom three exist, and you can forget them for now. byte and short save memory you don't need to save. float is a smaller, sloppier double. Stick to int, long, double, boolean, char.
That E308 means "and then 308 more digits' worth of zeroes". Enormous. But hold on to this: a big range is not the same as being exact. Section 8 is going to be rude about that.
Going deeper Why double gets 64 switches and float only 32
A fraction isn't stored as one long run of switches. It's stored in three parts: a sign, an exponent, and the digits. The exponent says where to put the point, which is how one small box reaches both tiny and gigantic numbers.
A float splits its 32 switches as 1 sign, 8 exponent, 23 digits. A double splits its 64 as 1, 11 and 52. More digit switches buys exactness. More exponent switches buys reach.
The names come straight from that. float was the normal size once, and a double is double the size. Java made double the default because 23 digit switches run out fast. Here's the sly part: 0.1f + 0.2f prints 0.3, which looks better than the double answer. It isn't. A float is simply too blunt to show you the error. Phase 3 pulls this layout apart switch by switch in C.
- Five types cover almost everything:
int,long,double,boolean,char. - Static typing means the kind of every box is decided beforehand, and checked by
javac. - An
intis 32 switches and tops out at 2,147,483,647. Alongis 64. - A whole number bigger than an
intneeds theLon the end:9000000000L. - A
chartakes single quotes,'B', and holds exactly one character.
Four declarations, straight out of Kinds.java — except one has been changed. Which line will javac refuse to build?
Section 6Everything is on/off switches
Count on your fingers. One hand gives you five numbers, because you're using each finger as a tally mark.
Now cheat. Don't tally — make each finger worth a different amount. Thumb is 1, index is 2, middle is 4, ring is 8, little finger is 16. Every finger is either up or down. How high can you count now?
Thumb alone is 1. Index alone is 2. Both is 3. Middle alone is 4. Middle and thumb is 5. Keep going, and every number from 0 to 31 has exactly one finger pattern. That's binary: each position is worth double the one before, and each is only ever on or off.
Computers work this way because a switch is easy to build and a "seven" is not. Electricity flows, or it doesn't. Written down, an up finger is 1 and a down finger is 0. src/Bits.java shows the switches behind some numbers:
number -> switches
0 -> 0
1 -> 1
2 -> 10
3 -> 11
5 -> 101
7 -> 111
13 -> 1101
255 -> 11111111
MAX_VALUE -> 1111111111111111111111111111111
MAX + 1 -> 10000000000000000000000000000000
which is -2147483648Check 13 by hand. Read 1101 right to left: 1 + 0 + 4 + 8, which is 13. And 255 is eight switches all on — the biggest eight switches can make. Which is exactly why a byte stops where it does.
Here's the sentence the whole chapter hangs on. A fixed number of switches can only make a fixed number of patterns. An int gets 32 switches, which is 4,294,967,296 patterns. Not one more, ever. Java spends half of them on negative numbers, so the top lands at 2,147,483,647.
You may have met that number without knowing it. In 2014 a certain music video passed two billion views, and YouTube announced it had outgrown its counter. Same 32 switches. (Widely reported at the time — treat the story as story. The number is the part we verified here.)
Section 7The surprise: riding off the end
Back to the bike counter. Six windows, so 999999 is the top, and one more metre rolls it to 000000. An int has 32 windows instead of six, and they're binary, but the principle is identical. So what happens when you add 1 to the biggest int there is?
Before you answer: this catches people who've been paid to write code for years. In 1996 it destroyed a rocket. The Ariane 5's guidance computer moved a number into a box too small to hold it. Make this a bet with yourself.
Java's biggest int is 2147483647. This program stores it, then prints it plus one. Four options: it prints 2147483648; it crashes with an error; it stays at 2147483647; or something else. Which — and if "something else", what?
big = 2147483647
big + 1 = -2147483648
small = -2147483648
small - 1= 2147483647
as long = 2147483648
byte 127+1 = -128Adding one to the biggest number gives you the smallest number. Not an error. Not a warning. Java doesn't even flinch — it hands you a wrong answer with total confidence. If you guessed "crashes", that was the sensible guess. Being wrong here is the most useful thing in this chapter.
This is called overflow, and the mechanism is the bike counter exactly. The biggest int in binary is 31 ones in a row. Add one and every switch flips, the way 999999 + 1 flips every digit. There's no 33rd switch to carry into, so the carry falls off the end. And Java reads the leftmost switch as the minus sign. So the pattern for "one past the top" is also the pattern for "the very bottom".
MAX_VALUE -> 1111111111111111111111111111111
MAX + 1 -> 10000000000000000000000000000000All ones, then a one followed by nothing but zeroes. The switches did the sensible thing. It's our reading of the leftmost one as "negative" that makes it a surprise.
"The leftmost switch is the minus sign" gives you the right answer at both edges. It isn't the whole rule, though. Java's real scheme has a name: two's complement. Under it, all 32 switches on means -1, not the biggest negative number. We ran that to check. The full rule is two lines long, so here it is — we're not going to hide it from you.
Going deeper Two's complement — the actual rule, not the shortcut
Here's the whole thing. To get the switches for a negative number, take the positive one, flip every switch, then add 1.
Try it on 1. One is 31 zeroes and then a single 1. Flip every switch and you get 31 ones and then a 0. Add 1 and it carries all the way, leaving 32 ones. So all switches on is -1. That isn't "the biggest negative" — it's just minus one.
That's why the shortcut misleads you in the middle. The leftmost switch really does decide the sign. But when it's on, the switches to its right aren't read as a plain number any more.
The rule also explains a lopsided detail. Zero eats one of the patterns that would otherwise be positive. So there's one more negative number than positive, and -2,147,483,648 has no positive twin. Which means Math.abs(Integer.MIN_VALUE) hands you back -2147483648. We ran that too, and yes, it's an absolute value that's negative.
So why this scheme instead of a plain minus sign? Because one adding circuit can then do subtraction as well. That reasoning is hardware, so it lands in Phase 3, where we get down to the metal in C.
Now look back at the other output lines. small - 1 wraps the other way, from the bottom straight to the top. byte 127+1 gives -128, because a byte has only 8 switches and runs out much sooner. And as long prints 2147483648 correctly — same sum, bigger box, no wrap.
Use a long when a count could plausibly pass two billion. Views, milliseconds since 1970, bytes in a file, money in pennies. It costs you 4 extra bytes and saves you a bug that produces no error message at all.
Do it with your own hands
Reading about the switches is one thing. Flipping them is another. Below are the 32 switches of a real int, and the number they add up to. Click any switch to turn it on or off.
Three things to try, in this order. Each one has an exact answer, so check the number on screen against the one written here.
- It starts at
MAX_VALUE, reading 2,147,483,647 — every switch on except the leftmost. Press add 1. Every single switch flips, and the number becomes -2,147,483,648. That's the smallestintthere is. - Press MAX_VALUE again, then click the leftmost switch on its own. All 32 switches are now on, and the number reads -1. One switch took you from two billion to minus one.
- Press -1 and count the switches it lights up. All 32 of them, every time. If the leftmost switch were just a minus sign, that pattern would be the biggest negative number instead.
That last one is worth sitting with. If the leftmost switch were simply a minus sign, then all switches on would be the biggest negative number there is. It isn't — it's -1. That's two's complement doing its thing: flip every switch, then add one. The rule's in the box above if you skipped it. And the circuitry it saves gets taken apart in Phase 3, in C.
- Overflow is silent. No error, no warning, just a confidently wrong answer.
- Add 1 to
Integer.MAX_VALUEand you land onInteger.MIN_VALUE. The counter wraps, both ways. - Negative numbers are stored as two's complement: flip every switch, add one. All ones is -1.
- A
bytewraps at 127, anintat 2,147,483,647. Fewer switches, sooner. - If a count could plausibly pass two billion, use a
longfrom the start.
Section 8Second surprise: doubles lie
So whole numbers can run out. Fractions have a different problem, and it's worse, because it shows up with tiny everyday numbers.
Try writing one third as a decimal. 0.3? Not quite. 0.333? Closer. You can add threes forever and never land on it. One third is a perfectly good number — base ten has no exact way to write it down.
Binary has the same problem with a different set of numbers. Base ten writes halves, quarters, fifths and tenths exactly. Base two writes halves, quarters and eighths exactly — but not fifths, and not tenths. So 0.1 is base two's version of one third. Java stores the closest pattern it can and moves on.
Two double boxes, one holding 0.1 and one holding 0.2. The program prints their sum, then prints whether that sum is equal to 0.3. What comes out? Read the paragraph above again first — it's a fair fight.
0.1 + 0.2 = 0.30000000000000004
is it 0.3? false
ten 10p coins = 0.9999999999999999
0.1 is really 0.1000000000000000055511151231257827021181583404541015625
ten 10p coins, in pennies = 100That fourth line is the whole story. When you type 0.1, that enormous number is what Java actually stores. It's the nearest pattern 64 switches can make. Two nearly-rights added together give a visibly-wrong.
Walk down that output. Line 1: the sum is off by 0.00000000000000004 — small, but not zero. Line 2: asking "is it 0.3" gives false. That's why you must never test doubles for exact equality. Line 3: ten 10p coins come to 0.9999999999999999. A till built this way is short of a pound. Line 4 is the confession. Line 5 shows the fix.
Never use double for money. Count in the smallest unit instead — pennies, cents — as int or long. £4.99 becomes 499. Whole numbers are exact, so your total is exact. (Java also has BigDecimal for this, which we'll meet much later.)
double is still right for heights, speeds, temperatures and averages. Anywhere an error in the sixteenth decimal place truly doesn't matter. It's wrong anywhere someone counts the pennies.
Section 97 / 2 is 3, and % is the bit left over
One more place where Java's answer isn't the one you learned in school. When both sides of a / are whole numbers, Java does whole-number division. The answer must fit a whole-number box, so anything after the decimal point is thrown away. Not rounded — thrown away.
Three lines: 7 / 2, then 7 % 2, then 7.0 / 2. The % sign is new — have a guess anyway, based on the name "remainder". What are the three answers?
7 / 2 = 3
7 % 2 = 1
7.0 / 2 = 3.5
10 / 3 = 3
10 % 3 = 1
9 % 3 = 0
each gets 3
left over 1
fair share 3.57 / 2 is 3. The 0.5 is gone, and no error is reported. Change one number to 7.0 and suddenly you get 3.5. Now one side is a fraction, so Java does fraction division.
The % is the remainder operator. Share 7 things between 2 people: / says each gets 3, and % says 1 is left over. Despite the symbol, it has nothing to do with percentages. You'll also hear it called modulo. That's just the maths name for the same job: the amount left over after a division. Same thing, older word.
Going deeper Why / chops instead of rounding, and what Math.round does differently
Rounding is a decision, and whole-number division isn't in the business of deciding. It answers a narrower question: how many whole 2s fit inside 7? Three do, and a bit is left over. That leftover bit is exactly what % hands you.
The pair is designed to fit together. (7 / 2) * 2 + (7 % 2) gives you 7 back, which we ran. Round the division instead and that stops being true, so / and % stop matching.
Chopping always goes toward zero, never downward. So -7 / 2 is -3, not -4. The remainder takes its sign from the left-hand side, so -7 % 2 is -1. Both of those ran on this machine.
Math.round is the opposite — deciding is its whole job. It adds a half and then chops, so halves go upward: Math.round(2.5) is 3 and Math.round(-3.5) is -3. We ran those too. One trap, though. It takes a fraction, so Math.round(7 / 2) can't rescue you. It prints 3, because the chopping already happened inside the brackets.
Two things % is brilliant at. "Is this even?" — n % 2 == 0. And "wrap around a fixed number of slots". day % 7 keeps you inside a week, however big day gets. You'll meet % again in Phase 2. There a hash table has to squeeze any number at all into a fixed number of slots. It's the same trick as the days of the week, doing a much bigger job.
You want the average of 3 test scores: 90, 85 and 88. You write int average = (90 + 85 + 88) / 3;. What do you get?
Section 10A letter is a number in a costume
Your computer stores no letters. It has switches, so it has numbers, so somebody had to agree which number means which letter. That agreement is a big lookup table. It puts capital A at 65, B at 66, and so on in order.
A char is that number. Java prints it as a letter to be helpful. Which leads somewhere strange.
The predict below uses one new piece of writing, so meet it first. A type name in round brackets, like (char), is a cast. It means "take this value and treat it as that kind instead". Written in front of a number, (char) says "read this one as a letter, please". You're the one asking, so you're the one taking responsibility.
If 'A' is really 65, what does Java print for 'A' + 1? Does it print B, or 66, or refuse? And what about (char)('A' + 1)?
letter = A
as a number = 65
'A' + 1 = 66
(char)('A'+1) = B
'a' as number = 97
'0' as number = 48
int -> double = 7.0
(int) 3.99 = 3
(int) -3.99 = -3
Math.round = 4'A' + 1 gives 66, not B. Doing maths on a char makes Java hand the result back as an int. So it prints as a number. Wrap it in (char) and you get B.
So the cast is what did the work. (char) took the plain 66 and said "print this one as a letter", and out came B. Leave the cast off and you get the number, because maths on a char hands back an int.
Casting goes two ways, and Java treats the two very differently. Moving a value into a roomier box is called widening — the box gets wider, so nothing can be lost. Java therefore does it silently. Every int fits inside a double with room to spare.
Going the other way, into a smaller box, is called narrowing. Now something might not fit, and whatever doesn't fit gets thrown away. So Java refuses to do it behind your back. You have to write the cast and sign for the loss.
int whole = 7;
double asDouble = whole; // widening: automatic
double messy = 3.99;
int chopped = (int) messy; // narrowing: you must askThe first line makes an int holding 7. The second copies it into a double. Nothing is asked of you, and it prints as 7.0. The third makes a double holding 3.99. The last one casts it to int, and the result is 3 — the .99 is deleted, not rounded. That (int) is you signing for the loss. From the output, (int) -3.99 gives -3, so a cast always chops toward zero. When you want real rounding, ask for it: Math.round(3.99) gives 4.
One more from that output: '0' is 48, not 0. The character zero and the number zero are different things.
- A
charis a number wearing a letter's face.'A'is 65 and'0'is 48. - Do maths on a
charand you get anintback, so'A' + 1prints 66. - A cast is a type in brackets:
(char),(int). It means "treat it as this kind instead". - Widening into a bigger box is automatic. Narrowing into a smaller box you must ask for.
- A cast chops toward zero, so
(int) -3.99is -3.Math.roundis what rounds.
Section 11final glues the box shut
Sometimes a value must never change. The number of lives in your game. The number of letters in a Wordle answer. If a later line changes it by accident, you'll hunt that bug for an hour. Put final in front of the declaration, and Java refuses to let anything re-assign it.
final int MAX_LIVES = 3;
System.out.println("lives allowed: " + MAX_LIVES);
MAX_LIVES = 4; // <-- DELETE THIS LINE to fix the fileLine 1 declares a box, fills it with 3, and glues the lid. Line 2 prints it, which is fine — reading is always allowed. Line 4 tries to re-assign it, and javac stops dead:
Finals.java:9: error: cannot assign a value to final variable MAX_LIVES
MAX_LIVES = 4; // <-- DELETE THIS LINE to fix the file
^
1 errorThe bug never runs, because the program never gets built. That's the whole point of catching things early. By habit, final values get SHOUTY_NAMES with underscores. You can see at a glance that they don't move.
Section 12Names: the rules and the manners
The rules are Java's, and breaking them is an error. A name can use letters, digits, _ and $. It can't start with a digit, so 2fast is out. No spaces, no dashes. It can't be a word Java reserves, like int or class. And it's case-sensitive: score and Score are two different boxes.
The manners are everyone else's. Java people write variable names in camelCase: first word lowercase, later words starting with a capital, no spaces. So viewCount, isGameOver, maxLives. The humps are the camel.
One piece of advice that outlasts this chapter: name the thing, not the type. secondsLeft tells the next reader something. x and myInt don't tell them anything. That reader is usually you, in three weeks.
You need a box for the number of lives a player has left. Which name will Java accept?
Open a terminal — the window where you type commands — and go to this chapter's src folder. Then work down the list. Predict each answer out loud before you press Enter. That habit is the whole course.
Section 13Check yourself
Three questions, and everything you need is on this page. The first two ask for a judgment rather than a fact, so say your reason out loud before you click. Every option tells you why it's right or wrong, including the ones you didn't pick.
You're storing the number of milliseconds a song has been playing. A three-minute song is 180000 milliseconds. Which type do you pick?
A friend says: "double is more accurate than int, because it holds decimals. I'll add up the shop's prices in doubles." Where does that go wrong, and what should they do instead?
You have char letter = 'M'; and you want to print the next letter along, N. Which line does it?
Section 14What broke, and what surprised you
You've met three error messages in this chapter and two silent wrong answers. The silent ones are the dangerous kind, so it's worth writing down which one got you.
Somewhere you can find again, jot down which predictions you got wrong and what you expected instead. Add any error message you hit that isn't on this page, copied exactly. Did your machine print something different from the outputs here? That mismatch is worth keeping. Every output above came from running the files in src/ on JDK 17.
The idea to carry forward is small. A value in Java always sits in a box of a fixed size. The box shapes what you get back. Next we stop storing values and start making decisions with them. And ==, the double equals we skipped, finally earns its keep.
- 12 Oct 2026 — The two
Bitslines had an extra space before->, and were labelled as the last two lines. They aren't —which is -2147483648comes after them. Both fixed. - 12 Oct 2026 — The
DoubleLiesoutput was missing the blank lines the program prints between its three experiments.
Nice. Chapter marked complete.