Chapter 7 — Reps
Conditioning, not grading. Type, compile, run. AI off.
Eighteen reps, and this week they are the whole game. You will read §7.1–§7.17 and think “I already know this” — right about the ideas, wrong about your fingers. Only typing closes that gap. Copy-paste teaches nothing this week.
Set up once: open onlinegdb.com, set the language to Java, and leave it there all week (Appendix A has the walkthrough). A local javac / java works too. Either path; don’t switch mid-rep.
Every rep ships with the exact output it should produce. Compare character for character — a line that differs is a real bug, not a “close enough.” Where a rep takes input, feed it exactly the sample input stated and you should get exactly the transcript shown.
Reps 1–6 are session 1, 7–12 session 2, 13–18 session 3, matching the chapter’s week-at-a-glance table. §7.19’s teasers are in here, placed where the difficulty curve wants them: Rep 1 is the same one, the Player class is Rep 7, the Motorcycle fleet is Rep 14.
Reps 1–6: Syntax boot camp
Session 1. Reading: §7.1–§7.8.
Rep 1 — Hello, from memory
Type Hello.java from memory, without looking at §7.2. Compile (javac Hello.java), run (java Hello). Repeat in a fresh file until the main signature comes out with zero typos first try. Do not stop at three.
Expected output:
Hello, world.
Now break it on purpose. Rename the file to Wrong.java, leave public class Hello alone, and compile — Bug 1 of §7.18, the most common first-day Java error:
Expected compiler error:
Wrong.java:1: error: class Hello is public, should be declared in a file named Hello.java
public class Hello {
^
1 error
Rename it back. Wording may vary slightly by compiler version; should be declared in a file named is the phrase to recognize. In OnlineGDB: your public class must be named whatever the file tab says.
Rep 2 — print, println, printf
Write Warmup.java. In main, in this order:
- Three
printlncalls printingSession 1: reps 1-6.,Session 2: reps 7-12.,Session 3: reps 13-18. - A run of
System.out.printcalls building one line out of pieces — the textSets:, the integer3, the textx, the integer12— thenSystem.out.println(" reps")to close it. System.out.printf("Cost per mile: $%.2f%n", 1.32);- A
printfwith%s,%d,%.1f, fed"Maya",12,72.5, producing the sentence below. - A plain
printlnof0.1 + 0.2glued onto the labelPlain println on a double:.
Expected output:
Session 1: reps 1-6.
Session 2: reps 7-12.
Session 3: reps 13-18.
Sets: 3 x 12 reps
Cost per mile: $1.32
Maya did 12 reps at 72.5 kg.
Plain println on a double: 0.30000000000000004
That last line is not your bug — it is §7.4’s point about doubles, and why anything money-shaped goes through printf with %.2f. Note %n is the newline inside a format string; println supplies its own.
Rep 3 — The five types, and the trap that survived
Write TypesRep.java. Give it one public static final double PASS_MARK = 70.0; at class level, then in main declare one of each of Java’s five everyday types:
int year = 2026;
double weight = 72.5;
boolean faithActive = true;
char category = 'G';
String verse = "1 Peter 3:15";
Print all six values on labeled lines. Then a blank line, then four String method results on verse: .length(), .indexOf(":"), .substring(2, 7), .toUpperCase(). Then a blank line, then year / 4 and year / 4.0. Pad the labels with spaces exactly as shown so you can diff by eye.
Expected output:
year = 2026
weight = 72.5
faithActive = true
category = G
verse = 1 Peter 3:15
PASS_MARK = 70.0
verse.length() = 12
verse.indexOf(":") = 9
verse.substring(2, 7) = Peter
verse.toUpperCase() = 1 PETER 3:15
year / 4 = 506
year / 4.0 = 506.5
Three things, all §7.4. substring(2, 7) gave five characters, not seven — start inclusive, end exclusive, unlike C++‘s substr(start, length). PASS_MARK printed 70.0, not 70, because it is a double. And year / 4 is 506 while year / 4.0 is 506.5 — integer division followed you across the language barrier. Compare with code/TypesDemo.java after, not before.
Rep 4 — Scanner, the safe way
Write Intake.java: reads three lines — name, age, weight in kg — and prints them back. Use the pattern §7.5 insists on: read every line as text with nextLine(), then parse it with Integer.parseInt / Double.parseDouble. Add a private static String readLine(Scanner in, String fallback) helper that checks in.hasNextLine() and returns the fallback at end of input, so an empty stdin box cannot crash you.
Prompt with System.out.print (no newline) for Name: , Age: , Weight in kg: . Then a blank line, the athlete’s name, the name’s length, the age plus one, and the weight through printf with %.1f.
Sample input (three lines):
Marcus
19
72.5
Expected output:
Name: Age: Weight in kg:
Athlete: Marcus
Name length: 6
Age next year: 20
Weight: 72.5 kg
The three prompts share one line because print adds no newline — that is correct, not broken.
Part 2 — feel the trap. In a scratch file, write §7.5’s six-line program that does in.nextInt() then in.nextLine(), printing age=[...] name=[...]. Feed it 19 then Marcus.
Expected output:
Age: Name: age=[19] name=[]
No crash, no warning, and the name is gone. That is Bug 12. Now you never have to be surprised by it.
Rep 5 — == vs .equals() (trace it by hand first)
Do not type this one first. Get paper. Predict all seven output lines, then write the program, then compare. Trace-by-hand-first is the highest-yield drill there is for the Part A code-reading exam.
Write EqualsRep.java:
String a = "hello";,String b = "hello";,String c = new String("hello");- Print
a == b,a == c,a.equals(b),a.equals(c), each on a labeled line. - Declare
int x = 5;andint y = 5;and printx == y. - Then construct a
Scanner,printthe promptType hello:, read one line intoString typed(guard withhasNextLine(), defaulting to"hello"), print an empty line, then printtyped == aandtyped.equals(a).
Sample input (one line): hello
Expected output:
a == b: true
a == c: false
a.equals(b): true
a.equals(c): true
x == y: true
Type hello:
typed == a: false
typed.equals(a): true
Count how many of the seven you got right. The last two matter most: you typed the same five characters and == said false. a == b was true only because the compiler pooled two identical literals into one object — an accident, not a rule. This is the bug that passes on your test data and fails on a real user’s typed input. Primitives use ==. Objects use .equals(). No exceptions.
Rep 6 — Arrays and ArrayList
Write Squad.java. Part 1, plain arrays (§7.8):
int[] scores = new int[5];, assign90to slot 0 and85to slot 1, printscores.length, then loop with a classic indexedfor, each slot indented two spaces.String[] roster = new String[3];— printroster[0]before assigning anything, then assign"Maya"and print it again.int[] primes = {2, 3, 5, 7, 11};— sum it with an enhanced-for (for (int p : primes)) and print the total.
Part 2, ArrayList (needs import java.util.ArrayList;): a blank line, then an ArrayList<String> of Maya, Marcus, Lin — print .size(), list them with an enhanced-for as - Name, remove "Marcus", print the new size, print the whole list object. Then an ArrayList<Integer>: add 12 and 15, print .get(0).
Expected output:
scores.length = 5
scores[0] = 90
scores[1] = 85
scores[2] = 0
scores[3] = 0
scores[4] = 0
roster[0] before assignment = null
roster[0] after assignment = Maya
sum of primes = 28
team.size() = 3
- Maya
- Marcus
- Lin
After remove: 2 players.
The whole list prints itself: [Maya, Lin]
first rep count = 12
scores.length has no parentheses; team.size() does; name.length() in Rep 3 did too. Java is not consistent here and you simply have to know which is which.
Part 3 — two quick breaks. Add scores[5] = 1; and run. Then, separately, declare ArrayList<int> nums = new ArrayList<>(); and compile.
Expected runtime exception (your at line will name your file and its own line number):
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
at Squad.main(Squad.java:6)
Expected compiler error:
B9.java:5: error: unexpected type
ArrayList<int> nums = new ArrayList<>();
^
required: reference
found: int
1 error
Java checked the bound and stopped; C++ would have shrugged and handed you nonsense. And generics hold objects only — ArrayList<Integer>, never ArrayList<int>.
Reps 7–12: The class model
Session 2. Reading: §7.7, §7.9, §7.11.
Rep 7 — Player
Two files. Player.java: a public class Player with private String name and int reps, a constructor taking both (use this.name = name; — the parameter shadows the field), a getter for each, and an @Override public String toString() returning "Maya: 12 reps" for new Player("Maya", 12). PlayerDemo.java: a main that constructs one and prints it with System.out.println(p);. Build with javac Player.java PlayerDemo.java, run with java PlayerDemo.
Expected output:
Maya: 12 reps
One line, four things proved: the two-file build worked, the constructor ran, toString() is right, and println(object) called it without being asked.
Part 2. In a second demo class, construct new Player("Maya", 12) and new Player("Marcus", 15), print both, print a line built from the getters, then glue a Player onto a String with +.
Expected output:
Maya: 12 reps
Marcus: 15 reps
Getter check: Maya has done 12 reps.
Glued into a String: Marcus: 15 reps
The last line is the second place Java calls toString() for free: string concatenation.
Rep 8 — Bug drill: what toString() is worth
Break Rep 7 twice.
Break 1. Delete the whole toString() method from Player. Recompile and run PlayerDemo.
Expected output (the eight hex digits after the @ are an identity hash — yours will differ from mine, and that is the entire lesson):
Player@1dbd16a6
That is Object’s inherited toString(), and seeing it in your output always means the same thing: you forgot to write one. Put it back.
Break 2. Now misspell it — keep @Override, rename the method toStrng(). Compile.
Expected compiler error:
Player.java:13: error: toStrng() in Player does not override or implement a method from a supertype
@Override
^
1 error
Wording may vary slightly by compiler version. Read what happened: the annotation caught a typo the compiler would otherwise have accepted in silence, leaving you a useless method and Object’s version still running. That is why §7.11 says write @Override every time. Free insurance, and you just watched it pay out.
Rep 9 — Account, migrated
Rebuild the Stewardship Account from P4 in Java, from this description — not by copying §7.9. Account.java: private double balance and String owner; a constructor clamping a negative starting balance to 0 with (starting >= 0) ? starting : 0; deposit(double) and withdraw(double) as guard clauses that print a rejection and return early; getBalance(); getOwner(); an @Override toString() built with String.format("[%s] balance: $%.2f", owner, balance).
AccountDemo.java: construct new Account("Maya", 100.0), then deposit(50.0), deposit(-25.0), withdraw(200.0), withdraw(80.0); print the object, the owner, and the raw balance.
Expected output:
Rejected: deposit must be positive.
Rejected: insufficient funds.
[Maya] balance: $70.00
Owner: Maya
Balance as a number: 70.0
100 + 50 − 80 = 70. The two rejected calls complained and changed nothing. Compare lines 3 and 5: $70.00 came from %.2f inside toString(); 70.0 is what a raw double looks like under println.
Part 2. In a second demo, construct new Account("Marcus", -50.0), print it, call deposit(0.0), withdraw(10.0), deposit(25.5), print it again, print the raw balance.
Expected output:
[Marcus] balance: $0.00
Rejected: deposit must be positive.
Rejected: insufficient funds.
[Marcus] balance: $25.50
Balance as a number: 25.5
The ternary caught the negative opening balance, 0.0 is not positive so the deposit was rejected, and you cannot withdraw 10 from 0.
Rep 10 — Two names, one object
Write ReferenceDemo.java alongside your Account. It needs two static helpers (they are called from main, which has no object — §7.15):
public static void tryToDouble(int x) // x = x * 2;
public static void reallyDeposit(Account acct) // acct.deposit(25.0);
In main: build Account a = new Account("Maya", 100.0);, then Account b = a; — no new. Deposit 50 through b, print a’s balance, print a == b. Build a separate Account c = new Account("Maya", 150.0); and print a == c. Call reallyDeposit(a), print the balance. Set int n = 5;, call tryToDouble(n), print n. Finally set a = null;, print a, print b.getBalance().
Expected output:
a's balance after b.deposit(50): 150.0
a == b (same object?): true
a == c (same object?): false
a's balance after reallyDeposit: 175.0
n after tryToDouble(n): 5
a after being set to null: null
b still holds it: 175.0
Read those seven lines slowly; they are §7.7 entire. Line 1: two names, one object, no copy. Line 3: c holds identical values and is still a different object — Rep 5’s lesson on a class you wrote. Line 4: objects passed to a method are reference-like, so the change stuck. Line 5: primitives are copies, so it did not. Lines 6–7: a is null, the object is fine because b still refers to it, and you wrote no delete and no destructor. When the last reference goes away the garbage collector reclaims it on its own schedule. You cannot call it and do not need to.
Part 2. Add one line right after the a == c line: print a.getBalance() == c.getBalance().
Expected: that line reads a.getBalance() == c.getBalance(): true. Two accounts, identical balances, == false on the objects and true on the primitives — three lines apart in one program.
Rep 11 — Bug drill: the array full of nulls
Write NullDrill.java. Declare Player[] squad = new Player[3]; and fill only slot 0 with new Player("Maya", 12). Print squad.length, print squad[1], then walk the array with an enhanced-for calling p.getName() and p.getReps().
Expected output (three lines, then it dies; your at line names your own file and line number, and the "<localN>" part may read "<local1>", "squad[1]", or similar depending on how the file was compiled):
squad.length = 3
squad[1] is: null
Maya has done 12 reps.
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Player.getName()" because "<local5>" is null
at NullDrill.main(NullDrill.java:10)
That is §7.18’s Bug 8, and modern Java is unusually kind about it: it names the method you called and the expression that was null. An array of objects is created full of null, not of default-constructed objects. Fill before you call.
Now fix it twice in one program: guard the loop with if (p == null) printing (empty slot); then fill slots 1 and 2 with new Player("Marcus", 15) and new Player("Lin", 9), print a --- all slots filled --- banner and the three players; finally set squad[0] = null; and print it.
Expected output:
squad.length = 3
squad[1] is: null
Maya has done 12 reps.
(empty slot)
(empty slot)
--- all slots filled ---
Maya: 12 reps
Marcus: 15 reps
Lin: 9 reps
squad[0] after being set to null: null
That last line is the garbage collector’s whole job description: the Maya object is now unreachable, and there is nothing for you to do about it.
Rep 12 — Vehicle, Car, Truck
Build the §7.11 hierarchy from scratch, four files: Vehicle.java, Car.java, Truck.java, and VehicleCheck.java with main.
Vehicle: protected int wheels, protected double weight; two-argument constructor; describe() printing A vehicle with N wheels.; costPerMile() returning weight / 1000.0; getWheels(); @Override toString() using String.format("%s(%d wheels, %.1f kg)", getClass().getSimpleName(), wheels, weight).
Car extends Vehicle: private int passengers; constructor whose first statement is super(wheels, weight);; @Override describe(); @Override costPerMile() returning super.costPerMile() * 1.1; getPassengers().
Truck extends Vehicle: private double cargoWeight; @Override describe() printing via printf with %.0f; @Override costPerMile() returning super.costPerMile() + (cargoWeight / 500.0); getCargoWeight().
In VehicleCheck.main, construct new Vehicle(2, 200), new Car(4, 1200, 5), new Truck(6, 3500, 2000) into variables of their own types (no polymorphism yet — that is Rep 13). For each, call describe() and print costPerMile() through printf with %.2f. Then println each object, then the Car’s inherited getWheels() and the Truck’s getCargoWeight().
Expected output:
A vehicle with 2 wheels.
Cost per mile: $0.20
A car carrying 5 passengers.
Cost per mile: $1.32
A truck hauling 2000 kg.
Cost per mile: $7.50
Vehicle(2 wheels, 200.0 kg)
Car(4 wheels, 1200.0 kg)
Truck(6 wheels, 3500.0 kg)
Car wheels (inherited getter): 4
Truck cargo: 2000.0
Look at lines 7–9. Car and Truck never wrote a toString(), yet each printed its own class name — getClass().getSimpleName() asked the object what it actually is at runtime. One inherited method, three answers.
Part 2 — the super(...) break. Write a Motorcycle extends Vehicle whose constructor takes only boolean hasSidecar and omits super(...). Compile.
Expected compiler error:
Motorcycle.java:4: error: constructor Vehicle in class Vehicle cannot be applied to given types;
public Motorcycle(boolean hasSidecar) {
^
required: int,double
found: no arguments
reason: actual and formal argument lists differ in length
1 error
Java tried to insert super() for you and Vehicle has no no-argument constructor. The required: line says exactly what super(...) wants. Keep this Motorcycle — Rep 14 finishes it properly.
Reps 13–18: Polymorphism
Session 3. Reading: §7.12–§7.17.
Rep 13 — FleetDemo (trace it by hand first)
Paper again, before you type. Given Rep 12’s three classes and this main, write all seven output lines by hand, dollar amounts included:
Vehicle[] fleet = {
new Car(4, 1200, 5),
new Truck(6, 3500, 2000),
new Vehicle(2, 200)
};
for (Vehicle v : fleet) {
v.describe();
System.out.printf(" Cost per mile: $%.2f%n", v.costPerMile());
}
double total = 0.0;
for (Vehicle v : fleet) {
total += v.costPerMile();
}
System.out.printf("Fleet total per mile: $%.2f%n", total);
System.out.println("Slot 0 says it is a " + fleet[0]);
Then write FleetDemo.java, build with javac Vehicle.java Car.java Truck.java FleetDemo.java, and run.
Expected output:
A car carrying 5 passengers.
Cost per mile: $1.32
A truck hauling 2000 kg.
Cost per mile: $7.50
A vehicle with 2 wheels.
Cost per mile: $0.20
Fleet total per mile: $9.02
Slot 0 says it is a Car(4 wheels, 1200.0 kg)
Score yourself honestly. The array is Vehicle[], every slot is typed Vehicle, the loop variable is typed Vehicle — and three different describe() bodies ran, because the declared type decides what you may call and the actual object decides which version runs. If you predicted A vehicle with 4 wheels. for line 1, you are still thinking in C++ values; reread §7.12 before Rep 14.
Check the arithmetic too: Car is 1200/1000 × 1.1 = 1.32, Truck is 3500/1000 + 2000/500 = 7.50, Vehicle is 200/1000 = 0.20, summing to 9.02.
Rep 14 — Add the Motorcycle
Finish the Motorcycle you broke in Rep 12. It extends Vehicle, takes (int wheels, double weight, boolean hasSidecar), calls super(wheels, weight) first, @Overrides describe() to print A motorcycle with a sidecar. or A motorcycle, solo., and @Overrides costPerMile() to return super.costPerMile() * 0.6. Add new Motorcycle(2, 200, false) as slot 3 of the fleet, rerun FleetDemo.
Expected output:
A car carrying 5 passengers.
Cost per mile: $1.32
A truck hauling 2000 kg.
Cost per mile: $7.50
A vehicle with 2 wheels.
Cost per mile: $0.20
A motorcycle, solo.
Cost per mile: $0.12
Fleet total per mile: $9.14
Slot 0 says it is a Car(4 wheels, 1200.0 kg)
The total moved from $9.02 to $9.14. If yours still says $9.02, you added the object to the array but the loop is not seeing it. Notice what you did not touch: not Vehicle, not the loop, not printf. A fourth kind of vehicle joined a working program and the program did not care. That is the payoff.
Part 2 — the enhanced-for’s one catch (§7.13). In a scratch class, build a two-slot Vehicle[] of a Car and a Truck. Loop it with an enhanced-for and reassign the loop variable (v = new Motorcycle(2, 200, true);). Print the array. Then do the same replacement with a classic indexed for writing to fleet[i], and print again.
Expected output:
After the reassigning loop:
Car(4 wheels, 1200.0 kg)
Truck(6 wheels, 3500.0 kg)
After the classic indexed loop:
Motorcycle(2 wheels, 200.0 kg)
Motorcycle(2 wheels, 200.0 kg)
The enhanced-for’s variable is a copy of the reference. Calling a method on it reaches the real object; reassigning it changes nothing. Replacing elements needs the indexed loop.
Rep 15 — Bug drill: the typo’d override
In your Car.java, delete the @Override above describe() and rename the method descrobe(). Leave everything else — including costPerMile() and its @Override — alone. Recompile the whole fleet. It compiles. Cleanly. No errors, no warnings. Run FleetDemo.
Expected output:
A vehicle with 4 wheels.
Cost per mile: $1.32
A truck hauling 2000 kg.
Cost per mile: $7.50
A vehicle with 2 wheels.
Cost per mile: $0.20
A motorcycle, solo.
Cost per mile: $0.12
Fleet total per mile: $9.14
Slot 0 says it is a Car(4 wheels, 1200.0 kg)
Line 1 changed and nothing else did. The Car still costs $1.32 because costPerMile() is still overridden, and still says Car(...) on the last line because toString() still dispatches. Only describe() fell back to Vehicle’s version, because descrobe is a brand-new method nothing calls. The most demoralizing bug in the chapter — one wrong letter, no error, and a program that runs and lies.
Now put @Override back on the misspelled method and compile.
Expected compiler error:
Car.java:9: error: descrobe() in Car does not override or implement a method from a supertype
@Override
^
1 error
Wording may vary slightly by compiler version. Fix the name, restore @Override, confirm line 1 reads A car carrying 5 passengers. again. Then add @Override to every override you have written this week.
Rep 16 — instanceof, the cast, and the crash
Write FleetReport.java over the four-vehicle fleet, running the same report twice — classic form, then Java 16+ pattern matching (§7.14) — separated by the banners below.
Classic: if (v instanceof Car) { Car c = (Car) v; ... }, else if (v instanceof Truck) { Truck t = (Truck) v; ... }, else print Plain vehicle, N wheels. from getWheels(). Pattern-matching: the same three branches as if (v instanceof Car c) / else if (v instanceof Truck t).
Expected output:
--- classic instanceof + cast ---
Car with 5 passengers.
Truck with 2000 kg of cargo.
Plain vehicle, 2 wheels.
Plain vehicle, 2 wheels.
--- Java 16+ pattern matching (check + cast in one) ---
Car with 5 passengers.
Truck with 2000 kg of cargo.
Plain vehicle, 2 wheels.
Plain vehicle, 2 wheels.
Both loops print the same four lines — that is the point; the second form folds the check and the cast into one move, and c exists only inside the if, where it is guaranteed valid. Now look at the two identical Plain vehicle, 2 wheels. lines: one is the Vehicle, one is the Motorcycle, which fell into the else because nobody added a branch for it. Hold that thought for Rep 17.
Part 2 — the crash. In a scratch class, cast without checking: Car bad = (Car) fleet[1]; where slot 1 is the Truck. It compiles.
Expected runtime exception (your at line names your own file and line):
Exception in thread "main" java.lang.ClassCastException: class Truck cannot be cast to class Car (Truck and Car are in unnamed module of loader 'app')
at BadCast.main(BadCast.java:10)
That is Bug 9. The compiler allowed it because fleet[1]’s declared type is Vehicle, which could be a Car; the JVM checked the actual type and refused. instanceof is what stands between you and this.
Rep 17 — Refactor the instanceof away
Rep 16’s report had a design smell wearing a keyword, and the two identical Plain vehicle lines are the symptom: every new subclass means another else if, and forgetting one is silent. Fix it the way §7.14’s Coach’s Note says — put the question in the hierarchy, where the object can answer it.
Add public int getCapacity() to Vehicle returning 0. @Override it in Car to return passengers, in Truck to return (int) (cargoWeight / 500.0), in Motorcycle to return 1. Then write CapacityReport.java: walk the four-vehicle fleet with an enhanced-for, print one line per vehicle with printf("%-12s capacity %d%n", v.getClass().getSimpleName(), v.getCapacity()), accumulate a total, print it. No instanceof. No casts.
Expected output:
Car capacity 5
Truck capacity 4
Vehicle capacity 0
Motorcycle capacity 1
Total capacity: 10
Count the lines you deleted. Four branches became one call, the Motorcycle stopped being invisible without anyone remembering it, and the next subclass will work on the day it is written. %-12s is left-justified in a 12-wide field — that is what lines the column up.
Rep 18 — static: the build log
Add class-level bookkeeping to Vehicle (§7.15): private static int totalBuilt = 0; incremented in the constructor, plus public static int getTotalBuilt().
Write BuildLog.java: print the total before constructing anything, through the class name as Vehicle.getTotalBuilt(). Then build the four-vehicle fleet, print the total again, print each vehicle indented two spaces, evaluate one more new Car(4, 900, 4); whose result you do not store, and print the total once more.
Expected output:
Before any vehicles: 0
After building the fleet: 4
Car(4 wheels, 1200.0 kg)
Truck(6 wheels, 3500.0 kg)
Vehicle(2 wheels, 200.0 kg)
Motorcycle(2 wheels, 200.0 kg)
After one more Car nobody kept: 5
Three things landed. Line 1 ran a method with no object in existence — that is what static buys, why main is static, and why you have used Math.sqrt, Integer.parseInt, and String.format all week without constructing anything. Line 2 says 4, not 1: every subclass constructor calls super(...), which is Vehicle’s constructor, which is the one counting. The last line says 5 because the counter is class-level bookkeeping, not object state — the fifth Car became garbage immediately and the count stands.
Part 2 — the rule that bites. In a scratch class, put a private int reps = 10; field and a non-static public int getReps(), then call getReps() bare from main.
Expected compiler error:
StaticDrill.java:7: error: non-static method getReps() cannot be referenced from a static context
System.out.println(getReps());
^
1 error
Precisely true: main has no object, so whose reps? Two fixes — construct one (new StaticDrill().getReps()), or make the helper static too. Helpers you call from main in the same class should be static.
Done? One Last Thing.
Blank file. Nothing else open — not this page, not the chapter, not code/. Write Lineup.java from scratch in the one-file pattern of §7.1 and §7.17: one public class matching the filename, every other class in the same file with no access modifier. This is the shape you will submit P6 in.
- A package-private class
Performer: aprotected String name(protected, not private — the subclasses print it), aprivate static int totalBooked = 0incremented in the constructor, apublic static int getTotalBooked(), agetName(), aperform()printing<name> takes the stage., and an@Override toString()returningString.format("%s: %s", getClass().getSimpleName(), name). - Three subclasses —
Musician,Athlete,Speaker— each with a one-line constructor callingsuper(name)and an@Override public void perform()printing, respectively,<name> plays a hymn on the piano.,<name> runs the 400.,<name> gives the defense. public class Lineupwith amainthat builds anArrayList<Performer>ofnew Musician("Lin"),new Athlete("Marcus"),new Speaker("Maya"),new Performer("Sam"); walks it with an enhanced-for callingperform(); prints a blank line; walks it again printing" " + p; then prints the booking count through the class name.
Compile the single file — javac Lineup.java — and run java Lineup.
Expected output:
Lin plays a hymn on the piano.
Marcus runs the 400.
Maya gives the defense.
Sam takes the stage.
Musician: Lin
Athlete: Marcus
Speaker: Maya
Performer: Sam
Booked: 4
One loop, four behaviors. One inherited toString(), four class names. One static counter, one number. If that came out of a blank file with no peeking, you have the move — and every piece P6 asks for.
If it did not, that is information, not failure. Whichever line went wrong names a section: wrong perform() running → §7.11 and Rep 15; Performer@... in the output → §7.9 and Rep 8; cannot find symbol on name → the access-level sidebar in §7.9; a filename error → §7.1. Re-drill that rep, then open a blank file and do this again tomorrow.
Up next: Take the §7.20 Checkpoint cold — closed book, 30 minutes, pass bar 6 of 8. Then open Project 6 and ship P6 — Java Migration & Polymorphic Fleet.