Phase 0 · Foundations

Give a piece of work a name

By the end you will write your own methods, know exactly where your variables live and die, and read a crash report line by line.

⏱ ~55 minutes🧩 Needs: 0.3💻 Code: src/

You've already asked someone else to do work for you. Every time you typed System.out.println("hi"), you were handing a job to code somebody else wrote and named. You didn't open it up. You didn't read it. You said its name, gave it a word, and it printed. Today you get to be that somebody else.

This is the chapter where your programs stop being one long list of instructions. It's also where you meet the wall of red text Java prints when a program dies. That wall isn't noise. It's a map.

By the end of this page

Section 1The same five lines, three times over

Here's a small program. It prints three boxes: one for a high score, one for a new round, one for game over. Read it and notice how you feel about it.

src/Repeat.java
// The annoying version. Same five lines, three times over.
public class Repeat {
    public static void main(String[] args) {
        System.out.println("+----------------+");
        System.out.println("|   HIGH SCORE   |");
        System.out.println("+----------------+");

        System.out.println("+----------------+");
        System.out.println("|   NEW  ROUND   |");
        System.out.println("+----------------+");

        System.out.println("+----------------+");
        System.out.println("|   GAME  OVER   |");
        System.out.println("+----------------+");
    }
}

Every line here is one you already know. Almost. Let's pay off two words you've been typing since your very first program. Nobody has told you what they mean yet.

A class is a named design for code and data that belong together. Not a box, not a folder — a design, like a blueprint for a house. Java insists that every method live inside one, so there's nowhere else main could go. Without a class the file won't compile at all. Right now that's honestly all a class does for you. Phase 1 is where classes become the whole point, because that's where you start building things from the design.

public answers a different question: who's allowed to use this? It means "anybody". Take it off main and your file still compiles fine. I tried it. But the java command then can't find main, so it prints Main method not found and quits. Deciding who may use what is called access control. It only starts to earn its keep in Phase 1, when you have classes with parts worth keeping private.

Going deeper The four visibility levels, and why main has no choice

Java has four levels of access. From most open to least, they run like this. public lets any code anywhere in. protected is next. No word at all lets in any code in the same package. A package is a named group of classes, and it's Phase 1's business. private is the tightest: only code inside this very class.

main has to be public because the thing calling it isn't your code. It's the java launcher, sitting outside your class entirely. It looks up a method with the exact shape public static void main(String[]). Anything less open than public and the launcher isn't allowed to look. So it reports that it found no main at all.

Your own methods in this chapter don't say public. They don't need to — every caller is in the same file, so the default level already lets them in. That's why the page shows static void banner(...) and not public static void banner(...).

The rest you've met. main is the block Java starts running first. Each println prints one line of text and then moves to the next line. Nine calls. Three of them are identical to three others.

Now imagine your boss walks in and says the boxes should use = signs instead of dashes. You have to change six lines. You'll get five of them right. That sixth one will sit there looking slightly wrong for a month. This is not a made-up worry. It's the most common way real programs get bugs.

Think of it like this

You'd never write out a full recipe for scrambled eggs inside every other recipe that includes scrambled eggs. Write it once, on its own card, and call the card "scrambled eggs". Then other recipes say make scrambled eggs. Improve the eggs and you improve one card — and every recipe that uses it gets better for free.

That card is what we're about to build. Java calls it a method: a named piece of work you can ask for by name. Below is the very same program with a card called banner.

src/Named.java
// The same output, but the work has a name now.
public class Named {

    static void banner(String text) {
        System.out.println("+----------------+");
        System.out.println("|   " + text + "   |");
        System.out.println("+----------------+");
    }

    public static void main(String[] args) {
        banner("HIGH SCORE");
        banner("NEW  ROUND");
        banner("GAME  OVER");
    }
}

Line by line. static void banner(String text) is the top of the card. It says the card is called banner. It also says whoever uses it must hand over one piece of text. The three println lines inside the curly brackets are the work on the card. The middle one glues your text into the middle of the box using +, which joins text together. Then main says the card's name three times, each time with different text.

Predict before you run

Nine println lines became three. Does Named print less than Repeat did? Write down what you think comes out, then find out.

$ java Named
+----------------+
|   HIGH SCORE   |
+----------------+
+----------------+
|   NEW  ROUND   |
+----------------+
+----------------+
|   GAME  OVER   |
+----------------+

Character for character, Repeat printed exactly that. If you guessed "less output", that's a sensible guess — the file got shorter, so why wouldn't the output? Because a method isn't a shortcut for the computer. It's a shortcut for you. Java still runs nine print lines. It runs the same three lines, three times.

The whole idea

A method doesn't make your program do less. It makes your program say less. One place to write the work. One place to fix the work.

Section 2Reading a method, word by word

Methods have a fixed shape, and every word in that shape is doing a job. Let's take one apart. Below is a method that doubles a number.

from src/Anatomy.java
static int doubled(int number) {
    int answer = number * 2;
    return answer;
}

Five words on that first line, and each one is a separate promise.

Going deeper What static really means, if you want it now

Here's the picture you're missing, and it's only missing because you haven't met objects yet. A class is a design. From one design you can build many separate things, and each one gets its own set of boxes. A Dog design might say every dog has a name. Build three dogs and you've got three names, one per dog.

A method that isn't static is attached to one of those things. It's allowed to read that thing's own boxes, which is exactly what makes it useful. So calling it without naming which thing you mean is a question with no answer: whose name did you want? Java catches that at compile time rather than letting you find out later.

A static method skips all of that. It hangs off the class, so there's exactly one of it, and it can't reach any individual thing's boxes. It only sees what you hand it. In this whole chapter that's a perfect fit — doubled needs a number, not a belonging.

Which is why main must be static. When you type java Anatomy, nothing has been built yet. The launcher has your class and nothing else. So the method it starts with must be one that belongs to the class. Chapter 1.2 comes back to this properly, with objects in front of you.

Inside the body, int answer = number * 2; makes a fresh box called answer and puts double the number in it. Then return answer; does two things at once, and both matter. It hands the value in answer back to whoever asked. And it stops the method immediately. Any lines after a return never run.

Why the immediate stop? Because handing back the answer is the method finishing. The method was asked one question, and it just answered it, so there's no one left to work for. Think of return as the door, not a note on the desk.

Now the other kind. Some work doesn't produce a value at all. The work happens, and nothing comes back. Printing is like that. You don't want a number back from a print. You want the words on the screen.

from src/Anatomy.java
static void shout(String word) {
    System.out.println(word.toUpperCase() + "!");
}

The return type here is void, which is Java's way of saying this hands nothing back. Not zero. Not empty text. Nothing at all. The body has one line, and it does three things. word.toUpperCase() makes an ALL-CAPS copy of the text. + "!" sticks an exclamation mark on the end. Then println prints the result. There is no return here, because there's nothing to hand back. When the body runs out of lines, the method is done.

Hold on, though. Where did toUpperCase come from? Nobody wrote it in your file. Look at the dot, because the dot is the whole answer. doubled(21) is a method you call by name, on its own. word.toUpperCase() is a method you call on a thing, and the dot means "ask this particular piece of text".

So yes. Every piece of text in Java shows up with a pile of methods already stuck to it. toUpperCase is one of them. You never made it. It arrived with the text. That's what System.out.println has been doing all along, too. out is a thing that knows how to print, and println is one of its own methods. Phase 1 explains why things carry their own methods around like this. It's where the dot stops looking like punctuation and starts looking like the point.

Think of it like this

Asking a friend "what's 21 doubled?" gets you an answer you can use — that's a return type. Asking a friend "shut the door" gets you no answer, only a shut door. That's void. Both are useful. Only one of them can be written down.

Predict before you run

Here's main from Anatomy.java. Three lines print. What are the three things you see? The middle one catches people.

from src/Anatomy.java
int x = doubled(21);
System.out.println(x);
System.out.println(doubled(5) + doubled(5));
shout("hello");
$ java Anatomy
42
20
HELLO!

The middle line is the interesting one. doubled(5) hands back 10. It's called twice, so we get 10 and 10, and the + adds them to 20. Lots of people write 1010 here, because they remember + gluing text together in the banner. Good memory, wrong situation. + glues when one side is text, and adds when both sides are numbers. Then shout("hello") hands nothing back, but it prints HELLO! anyway.

Two traps, both real

Promise an int and forget the return, and the compiler stops you. The compiler is the program that turns your text into something Java can run. You have a file for this in your src/. Run javac MissingReturn.java and you get:

$ javac MissingReturn.java
MissingReturn.java:7: error: missing return statement
    }
    ^
1 error

And try to print the result of a void method — there is no result — and javac VoidInPrintln.java says:

$ javac VoidInPrintln.java
VoidInPrintln.java:10: error: 'void' type not allowed here
        System.out.println(shout("hello"));
                                ^
1 error

Both errors are Java refusing to guess what you meant. That's a feature.

Worth remembering

Section 3The box gets a copy, not the box

Two words that sound like the same word, and aren't. The names in the method's brackets when you write it are the parameters. The actual values you hand over when you use it are the arguments. In doubled(int number), number is the parameter. In doubled(21), the 21 is the argument.

Now the part that matters. When you pass an argument, the method's parameter box gets a copy of the value. It does not get your box. Read this program, then bet on the answer.

from src/CopyBox.java
static void addTen(int n) {
    n = n + 10;
    System.out.println("inside addTen, n is " + n);
}

public static void main(String[] args) {
    int score = 5;
    addTen(score);
    System.out.println("back in main, score is " + score);
}

main makes a box called score holding 5. It calls addTen(score). Inside, n = n + 10; writes 15 into n, and the next line prints it. Then we're back in main, which prints score.

Predict before you run

Second line of output: is score 5 or 15? This one catches professionals who have switched languages. Guess, and mean it.

$ java CopyBox
inside addTen, n is 15
back in main, score is 5

Still 5. addTen never touched score. When the call happened, Java copied the number 5 out of score and put that copy in a brand new box called n. Adding ten changed the copy. If you guessed 15, you were picturing n as a second label on the same box. That's a very reasonable picture — and it's the one we have to replace.

Think of it like this

You photocopy a page of your notes and hand the copy to a friend. They scribble all over it. Your notes are fine. Passing an int to a method does exactly that.

Remember this one. In Phase 1 we meet a kind of value where the copy behaves very differently. The whole surprise depends on you being solid on this page first.

Section 4Variables born inside a method die inside it

A variable you declare inside a method exists only while that method is running, and only inside it. The word for "the region of code where a name means something" is scope. A variable declared in a method body has scope of exactly that body.

Think of it like this

Scope is the sticky note on your desk while you're working on one job. When the job is done, the note goes in the bin. Anyone who walks past your desk afterwards can't read it. It isn't hidden. It does not exist.

You have a file in your src/ that's meant to fail. makeSecret declares secret inside itself. Then main tries to print it.

from src/ScopeBroken.java — will not compile, on purpose
static void makeSecret() {
    int secret = 99;
    System.out.println("made a secret: " + secret);
}

public static void main(String[] args) {
    makeSecret();
    System.out.println(secret);
}

Notice makeSecret() has empty brackets. A method is allowed to need nothing. It makes secret, prints a message using it, and finishes. Then main asks for secret — and here's what Java says.

$ javac ScopeBroken.java
ScopeBroken.java:13: error: cannot find symbol
        System.out.println(secret);
                           ^
  symbol:   variable secret
  location: class ScopeBroken
1 error

Read that carefully, because it's a friendly error. cannot find symbol means "you used a name I've never heard of". symbol: variable secret names the word it couldn't find. location: class ScopeBroken says where it looked. The little ^ points at the exact spot. Java isn't being difficult. From main's point of view, the word secret has never existed.

This is why return exists. If main needs the secret, the method must hand it back.

Section 5The stack of plates

This is the question the whole chapter has been building to. Say main has a variable called n. A method it calls has its own n. That method calls a third one, which also has an n. How does Java keep them straight?

Think of it like this

Imagine a tall spike with plates on it, the kind you see in a canteen. You can only ever touch the top plate. When a method gets called, Java writes that method's variables on a fresh plate. Then it drops that plate on top. When the method finishes, the top plate comes off and goes straight in the bin. Whatever was written on it is gone.

That spike is called the call stack, and each plate is a frame. A frame holds one running method's own copies of its own variables. This is the real machinery. Everything about scope, copies and returns falls out of it.

Here's a tiny program with three methods and three variables all called n. It's deliberately confusing to read. And it isn't confusing at all once you can see the plates.

src/Frames.java
// Three methods, each with its own local variable called "n".
public class Frames {

    static int triple(int n) {
        int out = n * 3;
        return out;
    }

    static int tripleThenAddOne(int n) {
        int t = triple(n);
        return t + 1;
    }

    public static void main(String[] args) {
        int n = 4;
        int answer = tripleThenAddOne(n);
        System.out.println("n is still " + n);
        System.out.println("answer is " + answer);
    }
}

triple takes a number, multiplies it by three into a box called out, and hands out back. tripleThenAddOne takes a number, asks triple for the answer, keeps it in t, and hands back t + 1. main puts 4 in its own n, asks tripleThenAddOne, and prints both its n and the answer.

Now step through it. Take your time on this one — click through every step. This picture is the thing you're actually here to learn.

The call stack, one call at a time

Call stack — top plate first

main n = 4 · answer = ?

Printed so far

(nothing yet)

Step 1 — the program starts. Java puts one plate on the spike, for main. Line int n = 4; has run, so main's n holds 4. answer is declared on the next line, but nothing is in it yet.

Call stack — top plate first

tripleThenAddOne n = 4 · t = ?
main n = 4 · answer = ?

Printed so far

(nothing yet)

Step 2 — the call happens. tripleThenAddOne(n) copies the 4 out of main's n and drops a new plate on top. That plate has its own n, and its own copy of 4 in it. Two boxes, two plates, same name, no confusion. main is frozen part-way through a line now, waiting.

Call stack — top plate first

triple n = 4 · out = ?
tripleThenAddOne n = 4 · t = ?
main n = 4 · answer = ?

Printed so far

(nothing yet)

Step 3 — a call inside a call. int t = triple(n); copies that plate's 4 and stacks a third plate. Three plates, three variables named n, all holding 4 by coincidence. Only the top plate is being worked on. The two below it are paused.

Call stack — top plate first

triple n = 4 · out = 12
tripleThenAddOne n = 4 · t = ?
main n = 4 · answer = ?

Printed so far

(nothing yet)

Step 4 — work on the top plate. int out = n * 3; runs. It reads the n on this plate, so 4 × 3 = 12, and writes 12 into out. Nothing on the lower plates moved.

Call stack — top plate first

triple n = 4 · out = 12
tripleThenAddOne n = 4 · t = 12
main n = 4 · answer = ?

Printed so far

(nothing yet)

Step 5 — return throws the plate away. return out; hands the value 12 down to the waiting plate, and then triple's plate goes in the bin. Its n and its out no longer exist anywhere. The 12 survived because it was handed back. That's the only way anything gets out of a method.

Call stack — top plate first

tripleThenAddOne n = 4 · t = 12
main n = 4 · answer = 13

Printed so far

(nothing yet)

Step 6 — and again. return t + 1; works out 12 + 1 = 13, hands 13 down to main, and that plate is binned too. main finally finishes its line and puts 13 in answer. Look at main's n: still 4. It was never in reach of anything above it.

Call stack — top plate first

main n = 4 · answer = 13

Printed so far

n is still 4
answer is 13

Step 7 — back to one plate. The two println lines run. Then main ends, its plate comes off, the spike is empty, and the program stops. Every variable in the program has now been created and destroyed, in a strict order, on plates.

Predict before you run

Before you scroll: what are the two lines Frames prints? The first one is the test of whether the plates idea landed.

$ java Frames
n is still 4
answer is 13

If you said n is still 12, you had the three ns sharing one box. They never do. Three plates, three ns, and only values that get returned ever climb down the spike.

Worth remembering

Section 6Reading the wall of red text

Now the payoff, and it's a big one. When your program dies, Java prints the call stack. The whole spike, plate by plate, exactly as it was at the moment things went wrong. That printout is called a stack trace, and most beginners scroll past it in a panic. You're not going to, because you now know what a stack is.

Let's break something on purpose, deep down. Three methods, each calling the next.

src/Boom.java
// Blows up on purpose, three calls deep, so we can read a real stack trace.
public class Boom {

    static int divide(int top, int bottom) {
        return top / bottom;
    }

    static int average(int total, int count) {
        return divide(total, count);
    }

    static void report(int total, int count) {
        System.out.println("about to work out the average");
        System.out.println("average is " + average(total, count));
    }

    public static void main(String[] args) {
        report(60, 0);
    }
}

divide takes two whole numbers and hands back the first divided by the second. Notice it has two parameters, separated by a comma — methods can take as many as you like. average passes its two numbers straight on to divide. report prints a message, then prints whatever average hands back. main calls report(60, 0). Sixty divided by zero. In maths that has no answer. In Java it's a crash.

Predict before you run

Java is about to print four at lines naming four methods. Which method is on the first one — main, or divide?

$ java Boom
about to work out the average
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at Boom.divide(Boom.java:5)
	at Boom.average(Boom.java:9)
	at Boom.report(Boom.java:14)
	at Boom.main(Boom.java:18)

divide is first. The trace is printed top plate first — the same order as the spike. Lots of people expect main at the top, because main started everything. Understandable, and backwards.

Now let's read it properly, because you'll use this skill for the rest of your life.

So the trace answers two different questions. The top line says where. The lines under it say how we got there. And the real bug here is not on line 5 at all — dividing is fine. The bug is line 18, where somebody passed a count of zero. You found it by reading downwards.

Keep this habit

Read the message first, the top at line second, then walk down until you hit a method you wrote and a value you can question. Nine times out of ten the bug is where the bad value came from, not where it landed.

Worth remembering

Section 7Running out of plates

Time to break Java in a way you haven't seen yet. What happens if a method calls itself? Not a copy of itself — itself, by name, with nothing to stop it.

src/ForeverRecursive.java
public class ForeverRecursive {

    static void dig() {
        dig();
    }

    public static void main(String[] args) {
        System.out.println("digging...");
        dig();
        System.out.println("this line never runs");
    }
}

Four lines of real work. dig takes nothing, hands nothing back, and its entire body is a call to dig. main prints a word, calls dig, and has a line afterwards that we've cheekily labelled.

Predict before you run

Does this run forever and freeze your machine? Print nothing? Crash? Have a real guess — the reason matters more than the word.

$ java ForeverRecursive — 1026 lines, trimmed to the first few
digging...
Exception in thread "main" java.lang.StackOverflowError
	at ForeverRecursive.dig(ForeverRecursive.java:5)
	at ForeverRecursive.dig(ForeverRecursive.java:5)
	at ForeverRecursive.dig(ForeverRecursive.java:5)
	at ForeverRecursive.dig(ForeverRecursive.java:5)
	at ForeverRecursive.dig(ForeverRecursive.java:5)
… and ForeverRecursive.dig(ForeverRecursive.java:5) about a thousand more times. I trimmed it.
   Java stops printing the trace after 1024 lines, so even the printout gives up.

It crashed in a fraction of a second, and it crashed with a name you can now read: StackOverflowError. Every one of those identical lines is a plate. dig called dig, which put a plate on the spike, which called dig, which put a plate on the spike. The spike has a fixed height. It ran out of room. That's all "stack overflow" means — the plate stack overflowed.

How many plates fit? HowDeep.java catches the crash and counts. On this machine, two runs in a row gave ran out of room after 45683 calls and ran out of room after 47594 calls. Your number will differ, and differ again next time. The exact height is not a promise Java makes.

Going deeper Why the spike has a fixed height at all, and how to change it

The obvious question: why not let the stack grow as needed? Two reasons, both practical.

First, a stack has to be one unbroken run of memory. Frames sit right next to each other, and Java finds the top one by simple arithmetic on an address. That's fast precisely because it's dumb. Growing the stack would mean one of two bad things. Either you find room next door, which some other part of the program may already own. Or you copy the whole stack somewhere roomier, and that breaks every reference pointing into it.

Second, every thread gets its own stack. A thread is one independent line of work, and a real server may run thousands at once. If each stack could balloon, a single runaway method would eat the memory the other threads needed. A fixed ceiling turns "my machine froze" into "one thread crashed with a readable error". That's a much better day.

You can move the ceiling with the -Xss option, like java -Xss4m ForeverRecursive. Asking the JVM to print its own settings on this machine reports a default of 2048 KB per thread. Yours may well differ. It depends on the chip and the operating system. Treat any single number as a measurement, not a rule.

That also explains the wobble in the two counts above. The frames aren't all the same size. And Java has a part called the JIT. It quietly rewrites busy methods into faster machine code while the program runs, and it's rewriting dig the whole time. A rewritten method can use a different amount of space per frame. So the same program fits a different number of plates each time. Phase 4 takes the JIT apart.

Watch out

An Error is not the same as an Exception. Exceptions are problems your program might sensibly handle. StackOverflowError means the machinery your program runs on has broken. Don't try to cope with it — fix the code that caused it.

So a method calling itself is a disaster. Except it isn't. It's one of the most elegant tools you have, and the only thing missing above was a way to stop.

from src/CountdownRecursive.java
static void countdown(int n) {
    if (n == 0) {
        System.out.println("Liftoff!");
        return;
    }
    System.out.println(n);
    countdown(n - 1);
}

Line by line. if (n == 0) checks for the finish. If we're there, print Liftoff! and return. That's return in a void method with no value after it. Java allows that, and it means "I'm done, leave now". If n isn't 0, print it, then call countdown again with a smaller number. Smaller every time, so it must eventually hit 0.

Now you've seen it work, here's the word: a method that calls itself is doing recursion. The if that stops it is the base case. Recursion without a base case is ForeverRecursive.java. Recursion with one is a countdown, and each nested call is one more plate that gets binned on the way back down.

from src/CountdownRecursive.java
static int factorial(int n) {
    if (n <= 1) {
        return 1;
    }
    return n * factorial(n - 1);
}

Factorial of 5 means 5 × 4 × 3 × 2 × 1. The base case says factorial of 1 (or less) is 1. Otherwise, hand back n times the factorial of one less. Read that last line as a sentence and it's almost the maths definition. Running java CountdownRecursive gives 3, 2, 1, Liftoff!, then 5 factorial is 120.

Section 8One name, several shapes

You've been using this since your first program and never noticed. System.out.println works on text. It works on whole numbers. It works on decimals. How can one method take all of those? It can't. There are lots of methods, all called println, each taking a different kind of thing. That's overloading: same name, different parameter list.

the four methods in src/Overloaded.java
static void show(int n)            { System.out.println("an int: " + n); }
static void show(double d)         { System.out.println("a double: " + d); }
static void show(String s)         { System.out.println("a String: " + s); }
static void show(int a, int b)     { System.out.println("two ints: " + a + " and " + b); }

Four methods, one name. Java picks which one you meant by looking at what's in your brackets: how many things, and what kind. The return type isn't part of the decision — only the parameters. Now the last call in main is show('A'), with single quotes. That's a char — a single character. And no show(char) exists.

Predict before you run

Five calls: show(7), show(7.5), show("seven"), show(7, 8), show('A'). The last one has no matching method. Does it fail to compile — or does something odd happen?

$ java Overloaded
an int: 7
a double: 7.5
a String: seven
two ints: 7 and 8
an int: 65

It compiled, ran, and printed an int: 65. Wait, what? Remember from 0.2 that a character is stored as a number. Capital A is number 65. With no show(char) to call, Java went hunting for the closest thing it could reach. A char box is smaller than an int box. So the 65 slides into an int with room to spare and nothing lost. Moving a value into a roomier type is called widening. Java does it without asking you first. That's how show(int) ended up with the call. Overloading is genuinely handy, and this is its sharp edge — "closest" isn't always what you meant.

Going deeper Why overloading ignores the return type

It looks like an oversight, doesn't it? Two methods called show, one handing back an int and one a String, should be tellable apart. Java rejects that pair outright. Here's why.

Java decides which overload you meant by reading your call — the name, and what's in the brackets. That's all the information a call carries. Now look at show("hi"); on a line by itself. Nothing there says what you wanted back, because you threw the answer away. If the return type were part of the choice, that line would be a genuine tie. Java would have nothing to break it with.

You might say "use the type of the variable I'm assigning to". That works right up until the call sits inside another call. In show(pick(3)) the answer feeds straight into show. So choosing a show would need to know which pick was chosen. And choosing pick would need to know which show applies. The rule would chase itself in circles.

So the line is drawn firmly: overloads must differ in the parameter list. Number of parameters, or their types, or their order. Try changing only the return type and the compiler says method show(String) is already defined in class. To Java, you wrote the same method twice.

Section 9The guessing game, with names

Go and look at src/GuessOld.java. It's the guessing game from 0.3, secret fixed at 42 so your runs match this page. Everything is in one lump inside main: the rules, the loop, the reading, the range check, the hints, the ending. Thirty-odd lines you have to read all of to understand any of.

Nothing in it is wrong. It doesn't say anything, though. Compare the middle of GuessNamed.java:

from src/GuessNamed.java
printRules();

while (tries < 5) {
    int guess = askForGuess();
    tries = tries + 1;

    if (isSilly(guess)) {
        System.out.println("That is not between 1 and 100.");
    } else if (guess == secret) {
        System.out.println("Got it in " + tries + " tries!");
        won = true;
        break;
    } else {
        giveHint(guess, secret);
    }
}

You can read that out loud and it's nearly English. Print the rules. While there are tries left, ask for a guess. If the guess is silly, say so. If it's the secret, celebrate and stop. Otherwise give a hint. Four method names — printRules, askForGuess, isSilly, giveHint — and the shape of the game is visible at a glance.

Two of those are worth a second look. askForGuess is static int askForGuess(): no parameters, but it hands back an int. It prints the prompt and returns whatever was typed. And isSilly returns boolean — true or false. A method that hands back a yes-or-no is how you give a condition a name. Your if then reads like a sentence instead of a puzzle.

$ java GuessNamed — typing 50, 25, 999, 42
I picked a number from 1 to 100.
You get 5 guesses.
Guess: Too high.
Guess: Too low.
Guess: That is not between 1 and 100.
Guess: Got it in 4 tries!

Identical behaviour to the old version, and the new one is easier to change. Want three guesses instead of five? One number. Want a different hint? One method. That's the win.

Your turn

Everything below is in your src/ folder. Run javac on a file, then java on its name. You'll need to compile the three "meant to fail" files one at a time. Running javac *.java would stop on the first one.

Section 10Three questions

A method's first line is static void save(String name, int score). Which statement is true?

Your program dies and the trace starts:
at Store.load(Store.java:31)
at Store.open(Store.java:12)
at Store.main(Store.java:4)
Where did the program actually stop?

Judgment call. A friend's program crashes with StackOverflowError. They ask you to wrap the call in something that catches the error so the program keeps going. What's the better advice?

Section 11What broke, and what surprised you

Write down two things before you move on. The error message you saw the most today, in your own words. And the moment you thought Java would do one thing and it did another.

If an int: 65 was your moment, good — that's Java making a quiet decision for you. Spotting those is most of what getting good at this means. If your moment was score is 5, hold on to it. Phase 1 turns that one upside down.

Next up: one name holding many values at once, and what happens when you ask for the twenty-first item in a list of twenty.

Nice. Chapter marked complete.