Start Guide (≈ 5 minutes)
This workshop uses OnlineGDB – a free, modern, browser‑based IDE that supports multiple Java files and a full debugger (breakpoints, stepping, variable watch, call stack) with no paywall.
You can follow these steps on any computer with a web browser.
1. Open the IDE
- Open your browser and go to
https://www.onlinegdb.com/online_java_compiler. - You should see a Java file named
Main.javain the main editor and a Run and Debug button at the top.
Instructor note (1 min): Make sure everyone is on the site and sees the Java editor.
2. Create the project layout
We will work with several small Java files.
- In the File panel (left side), click New File.
- Name the file exactly:
BuggyCalculator.java. - Repeat and create these files as well:
BuggyLoop.javaGameApp.javaPlayer.javaScoreUtils.javaLevelLoader.javaInputParser.java
- Keep the default
Main.javafile for now – we will replace its contents later.
OnlineGDB saves these files in the temporary project in your browser.
3. How to paste in the starter code
Each exercise chapter in this workshop includes copy‑paste‑ready code blocks.
For each file:
- Click the file name in the left panel (for example
BuggyCalculator.java). - Select all existing code in the editor and delete it.
- Copy the corresponding code block from the workshop and paste it into the editor.
- Press Ctrl+S (or use the Save icon) to save that file.
Tip: Double‑check the file name at the top of the editor matches the heading above the code block in the book.
4. Running the program
OnlineGDB runs whichever file contains the public static void main(String[] args) method.
In this workshop, Main.java (or GameApp.java, when specified) will be the entry point.
To run:
- Make sure the correct main file (for example
Main.java) is open in the editor. - Click the Run button at the top.
- Watch the Console panel at the bottom for:
- Program output
- Error messages
- Stack traces (when exceptions occur)
If the program doesn’t compile, error messages will appear in the console with line numbers.
5. Starting a debugging session
To start the debugger in OnlineGDB:
- Open the file that contains
main(for exampleMain.javaorGameApp.java). - Click in the gutter (the narrow column to the left of the line numbers) next to the line where you want execution to pause.
- A red dot appears – this is a breakpoint.
- Click the Debug button at the top.
- When the program hits a breakpoint, it will pause and highlight the current line.
6. Stepping through code
When the program is paused on a breakpoint, use the toolbar buttons above the editor:
- Step Over: runs the current line and stops at the next line in the same method.
- Use this when you do not need to go inside a method call.
- Step Into: jumps into the method being called on the current line.
- Use this to enter helper methods in other files/classes.
- Step Out: runs the rest of the current method and returns to the caller.
- Use this to quickly leave a method once you understand it.
You can also press Continue to run until the next breakpoint or program exit.
7. Inspecting variables, watches, and call stack
When stopped on a breakpoint:
- Variables / Locals panel:
- Shows the values of local variables in the current method.
- Expands objects so you can see their fields (for example,
player.name,player.score).
- Watch expressions:
- Use the Watch panel (if visible) to add an expression (e.g.,
scores.size()) and track its value as you step.
- Use the Watch panel (if visible) to add an expression (e.g.,
- Call stack:
- Shows the chain of method calls that led to the current line.
- Clicking on a stack frame jumps to that frame’s source (often in another file).
- Console:
- Shows printed output (
System.out.println) and error messages. - Useful for comparing print debugging with debugger‑based debugging.
- Shows printed output (
You will use all of these while working through the Levels in this workshop.
8. If something goes wrong
If the debugger seems stuck or weird:
- Click Stop to end the current run.
- Re‑click Debug.
- Make sure your breakpoints are on executable lines (not on blank lines or comments).
If the code doesn’t look like this book:
- Re‑copy the code from the chapter into the right file.
- Confirm the file name and class name match exactly.
Once everyone can set a breakpoint and step a line in Main.java, you are ready for Level 1.
Workshop Overview (≈ 5 minutes)
Total time goal: ≈ 50 minutes
- Start Guide – 5 minutes (already completed)
- Level 1 – Reading Errors – 10 minutes
- Level 2 – Debugger Basics – 12 minutes
- Level 3 – Multi‑File Debugging – 13 minutes
- Level 4 – Layered Bugs – 10 minutes (stretch / as time allows)
Goals
By the end of this workshop, you should be comfortable with:
- Finding and understanding:
- Compile‑time errors
- Runtime exceptions
- Logic bugs (wrong behavior, no crash)
- Using the debugger (not just
System.out.println):- Setting and using breakpoints
- Stepping over / into / out of methods
- Inspecting variables and object state
- Reading the call stack to trace where a bug starts
- Working with multiple files:
- Stepping into helper classes
- Following control flow across files
- Fixing one bug without introducing another
Structure and pacing
Each Level follows a similar pattern:
- Setup – copy/paste the starter code into the right file(s).
- Run and observe – see what error or wrong behavior appears.
- Investigate – use the debugger (and sometimes print statements) to narrow down the issue.
- Fix – change the code to correct the bug.
- Reflect – short questions to connect the bug to a concept.
Instructor suggestion:
- Pause briefly between Levels to:
- Ask a couple of check‑in questions.
- Share a “live” debugging demo if many students are stuck.
Solutions for all Levels are collected in the Solutions & Fixes chapter so you can review them after class (or use them as backup during class).
Level 1 – Reading Errors (≈ 10 minutes)
Concepts: compile‑time vs runtime errors, error messages, stack traces, print debugging vs debugger.
We will start with two small programs:
BuggyCalculator.java– has compile‑time and runtime issues.BuggyLoop.java– has a logic bug (off‑by‑one).
1. Setup the files
Create or open these files in OnlineGDB and paste in the code below.
BuggyCalculator.java
public class BuggyCalculator {
// Intentionally buggy method: sometimes crashes, sometimes returns wrong result
public static int divide(int a, int b) {
// BUG 1: possible division by zero (runtime error)
return a / b;
}
public static int sumFirstThree(int[] numbers) {
// BUG 2: potential ArrayIndexOutOfBoundsException
int total = 0;
for (int i = 0; i <= 3; i++) { // off‑by‑one AND assumes at least 4 elements
total += numbers[i];
}
return total;
}
}
BuggyLoop.java
public class BuggyLoop {
// Intentionally buggy method: tries to count from 1 to n
public static int countUpTo(int n) {
int count = 0;
// BUG 3: incorrect loop condition, never reaches n
for (int i = 1; i < n; i++) {
count++;
}
return count;
}
}
Main.java
Replace the contents of Main.java with:
public class Main {
public static void main(String[] args) {
// Example 1: division
System.out.println("Trying division...");
int result = BuggyCalculator.divide(10, 0);
System.out.println("Result: " + result);
// Example 2: sum first three
int[] nums = {1, 2};
System.out.println("Trying sumFirstThree...");
int sum = BuggyCalculator.sumFirstThree(nums);
System.out.println("Sum: " + sum);
// Example 3: loop
System.out.println("Trying countUpTo(5)...");
int count = BuggyLoop.countUpTo(5);
System.out.println("Count: " + count);
}
}
2. Run and observe
- Open
Main.java. - Click Run (not Debug yet).
- Watch the Console output and error messages.
Expected symptoms:
- The program should throw a runtime error (an exception) on the division line.
- You should see a stack trace in the console mentioning:
- The type of exception.
- The line number in
BuggyCalculator.java. - The line number in
Main.javawhere it was called.
If you fix that and run again, you will later see:
- Another exception from
sumFirstThree. - A logic bug:
countUpTo(5)prints the wrong count but does not crash.
3. Read the error messages
Focus on the first failure (division by zero):
- Look for the exception type (e.g.,
java.lang.ArithmeticException). - Find the message (e.g.,
/ by zero). - Find the topmost line from your code in the stack trace:
- It should show
BuggyCalculator.dividewith a line number. - It should show
Main.mainwith a line number.
- It should show
Answer (to yourself or a neighbor):
- Is this a compile‑time error or a runtime error? Why?
- Which line in which file is the “root cause” line?
4. Quick detective work (no debugger yet)
Without changing anything, ask:
- Why might
bbe zero individe? - Why might
sumFirstThreehit an out‑of‑bounds index?
Use print debugging:
- Add
System.out.println("a=" + a + ", b=" + b);at the start ofdivide. - Add
System.out.println("numbers length=" + numbers.length);at the start ofsumFirstThree. - Run again and see the printed values before the crash.
5. Fix the obvious runtime bugs
Make the program stop crashing, but don’t worry about perfect logic yet.
Suggested changes (try on your own first):
- In
divide, guard againstb == 0:- Print an error and return 0, or
- Throw a more descriptive exception.
- In
sumFirstThree, handle arrays with fewer than 3 elements.
Once you have fixed the crashes:
- Comment out or remove the temporary
printlncalls you added. - Run again and confirm there are no exceptions.
6. Find the logic bug
Now focus on BuggyLoop.countUpTo(5):
- What do you expect
countUpTo(5)to return? - What does it actually print?
- Is this a compile‑time error, runtime error, or logic bug?
You can:
- Add a
printlninside the loop to printieach time. - Or (in the next Level) use the debugger to step through it.
7. Wrap‑up questions (Level 1)
Discuss or write short answers:
- What is the difference between a compile‑time error and a runtime exception?
- How does the stack trace help you find the root cause?
- When is print debugging helpful, and when might it be slower than using a debugger?
You will revisit these same bugs in Level 2, but this time with breakpoints and stepping.
Level 2 – Debugger Basics (≈ 12 minutes)
Concepts: breakpoints, stepping over/into/out, variable inspection, tracking changing state, logic bugs.
We will reuse the Level 1 code, but now solve the bugs with the debugger instead of just reading messages.
1. Confirm your code
You should still have:
BuggyCalculator.javaBuggyLoop.javaMain.java
If you already fixed the code in Level 1, temporarily undo those fixes (or paste the original versions from Level 1) so that:
dividecan still divide by zero.sumFirstThreestill loops toi <= 3.countUpTostill usesfor (int i = 1; i < n; i++).
We want the bugs back so we can practice debugging them.
2. Set your first breakpoint
- Open
Main.java. - Click in the gutter on the line:
int result = BuggyCalculator.divide(10, 0);
- You should see a red dot (breakpoint) next to that line.
Start the debugger:
- Click Debug.
- When execution pauses, the breakpoint line will be highlighted.
3. Step over and inspect variables
When paused at the breakpoint:
- Look at the Variables/Locals panel:
- You should see
argsand any local variables.
- You should see
- Click Step Over:
- This executes the
dividecall and moves to the next line (or throws an exception).
- This executes the
If an exception is thrown:
- Notice how the debugger shows the line that crashed.
- Compare to the stack trace from Level 1.
Repeat, but this time:
- Set a breakpoint inside
BuggyCalculator.divide, on the linereturn a / b;. - Start Debug again.
- The debugger will stop inside
divide.
Use the debugger to answer:
- What are the values of
aandbright before the division? - What happens if
bis zero?
4. Step into another method
Practice Step Into and Step Out with sumFirstThree:
- Set a breakpoint in
Main.mainon:int sum = BuggyCalculator.sumFirstThree(nums);
- Start Debug.
- When paused on this line, click Step Into.
You should now be inside BuggyCalculator.sumFirstThree.
While stepping through the loop:
- Watch
i,total, andnumbers.lengthin the Variables panel. - Use Step Over to run each iteration.
- Notice when
ibecomes an invalid index.
Optional: add a Watch expression (if available) for numbers[i].
When you understand why it crashes:
- Click Stop to end the debugging session.
- Fix
sumFirstThreeso it:- Does not crash when the array is short.
- Still correctly sums up to the first 3 items when they exist.
5. Debugging a logic bug with stepping
Now fix BuggyLoop.countUpTo using the debugger, not just by staring at the code.
- Set a breakpoint in
Mainon:int count = BuggyLoop.countUpTo(5);
- Click Debug.
- When paused, click Step Into to enter
countUpTo. - Use Step Over to step through each loop iteration.
Observe:
- How many times does the loop run?
- What are the values of
iandcountat each step?
Use this observation to:
- Decide what the loop should do.
- Update the loop condition so
countUpTo(5)returns the expected value.
Re‑run with the debugger to confirm the fix.
6. Compare debugger vs print debugging
Briefly discuss or think about:
- When was the debugger faster or clearer than
System.out.println? - When might print debugging still be useful?
- How did stepping into
sumFirstThreeandcountUpTohelp you see the bug more clearly?
You are now ready to debug across multiple files and classes in Level 3.
Level 3 – Multi‑File Debugging (≈ 13 minutes)
Concepts: stepping between files, multi‑file logic bugs, call stack, unexpected control flow, bad method arguments.
We will work with a tiny “scoreboard” app:
GameApp.java– main program that simulates a few players.Player.java– represents a player and their scores.ScoreUtils.java– helper methods with bugs (logic + bad arguments).
1. Setup the files
Create or open these files and paste in the code.
Player.java
public class Player {
private String name;
private int[] scores;
public Player(String name, int[] scores) {
this.name = name;
this.scores = scores;
}
public String getName() {
return name;
}
public int[] getScores() {
return scores;
}
}
ScoreUtils.java
public class ScoreUtils {
// BUG 1: incorrect average calculation (logic bug)
public static double averageScore(int[] scores) {
int total = 0;
for (int i = 0; i < scores.length; i++) {
total += scores[i];
}
// BUG: divides by (length + 1), making average too small
return (double) total / (scores.length + 1);
}
// BUG 2: unexpected control flow when scores is empty
public static int bestScore(int[] scores) {
// BUG: returns 0 even if there are negative scores or empty array
int best = 0;
for (int score : scores) {
if (score > best) {
best = score;
}
}
return best;
}
// BUG 3: bad method arguments / null handling
public static int totalScore(Player player) {
// BUG: does not guard against null player or null scores
int sum = 0;
for (int s : player.getScores()) {
sum += s;
}
return sum;
}
}
GameApp.java
public class GameApp {
public static void main(String[] args) {
Player alice = new Player("Alice", new int[] {10, 15, 20});
Player bob = new Player("Bob", new int[] {5, 7});
Player charlie = new Player("Charlie", new int[] {}); // empty scores
System.out.println("=== Scoreboard ===");
printPlayerStats(alice);
printPlayerStats(bob);
printPlayerStats(charlie);
// Intentionally pass a null player to show a null‑related bug
Player mystery = null;
System.out.println("\nMystery total score (should handle null):");
int total = ScoreUtils.totalScore(mystery);
System.out.println("Total: " + total);
}
private static void printPlayerStats(Player player) {
int[] scores = player.getScores();
double avg = ScoreUtils.averageScore(scores);
int best = ScoreUtils.bestScore(scores);
int total = ScoreUtils.totalScore(player);
System.out.println("Player: " + player.getName());
System.out.println(" Average: " + avg);
System.out.println(" Best: " + best);
System.out.println(" Total: " + total);
System.out.println();
}
}
Make sure:
- All three files are saved.
GameApp.javacontains amainmethod and will be your entry point.
2. Run and observe
- Open
GameApp.java. - Click Run.
Expected symptoms:
- The program may:
- Print suspicious/incorrect averages.
- Print wrong best scores (especially for negative or empty sets).
- Crash with a
NullPointerExceptionwhen using themysteryplayer.
Focus on:
- Which output looks wrong?
- Where does the stack trace say the null pointer came from?
3. Step across files with the debugger
Now use the debugger to see how control flows between files.
- Set a breakpoint on the first line of
maininGameApp. - Click Debug.
- When paused, click Step Over a few times to:
- Create the players.
- Reach the first call to
printPlayerStats.
- When you reach
printPlayerStats(alice);, click Step Into.
You should now be in printPlayerStats:
- Step until you reach
double avg = ScoreUtils.averageScore(scores);. - Use Step Into to jump into
ScoreUtils.averageScore. - Watch
scores.length,total, and the returned value in the Variables panel.
Answer:
- Why is the average too small?
- Which line in which file is the true bug?
4. Use the call stack to reason about a crash
Trigger the NullPointerException on mystery:
- Set a breakpoint on the line
int total = ScoreUtils.totalScore(mystery);inmain. - Start Debug and let execution run to that breakpoint.
- Step Into to enter
ScoreUtils.totalScore. - When the exception occurs, look at:
- The current line.
- The Call Stack panel.
Questions:
- Which expression is actually null?
- How did that null value get there? (Use the call stack to go “up” one frame.)
Fix totalScore so that:
- It does not crash when
playeris null orplayer.getScores()is null. - It returns a reasonable value (for example,
0) in those cases.
Run again to confirm the crash is gone.
5. Fix the best score logic
Currently, bestScore assumes:
beststarts at0.- Any score greater than
0is the best.
This fails when:
- All scores are negative.
- The array is empty.
Use breakpoints and stepping to:
- Pause inside
bestScore. - Watch how
bestchanges as you loop throughscores. - Decide what should happen when
scoresis empty.
Update bestScore to:
- Handle empty arrays gracefully (for example, return
0or a sentinel likeInteger.MIN_VALUE). - Correctly track the maximum value even when all scores are negative.
Re‑run the program and check:
- Do the printed “Best” values now match your expectations?
6. Reflection (Level 3)
Discuss or write:
- How did stepping between files help you understand the bugs?
- When reading a stack trace, how do you choose which frame to investigate first?
- How can you prevent null‑related bugs like the one in
totalScore?
In Level 4, you will tackle a more layered scenario that combines several types of bugs.
Level 4 – Layered Bugs (≈ 10 minutes, stretch)
Concepts: layered bugs, unexpected control flow, combining debugger tools, fixing bugs without breaking other behavior.
This Level uses a small “level loader” that:
- Reads a level number from a (fake) string.
- Loads some enemy counts.
- Computes total difficulty.
The code has:
- A runtime bug (exception).
- A logic bug.
- A bug where stepping into another file is useful.
1. Setup the files
We’ll add two more files:
LevelLoader.javaInputParser.java
InputParser.java
public class InputParser {
// Parses level from an input string like "level:3"
public static int parseLevel(String input) {
// BUG 1: assumes the string always has "level:" and a valid number
String[] parts = input.split(":");
String numberPart = parts[1]; // can throw if input is wrong
// BUG 2: doesn't trim spaces, can cause NumberFormatException
return Integer.parseInt(numberPart);
}
}
LevelLoader.java
public class LevelLoader {
// Returns an array of enemy counts for the given level.
public static int[] loadEnemiesForLevel(int level) {
if (level == 1) {
return new int[] {2, 3};
} else if (level == 2) {
return new int[] {4, 4, 5};
} else if (level == 3) {
return new int[] {10};
} else {
// BUG 3: unexpected control flow – returns null instead of empty array
return null;
}
}
// Computes a "difficulty" score from enemy counts.
public static int computeDifficulty(int[] enemies) {
int difficulty = 0;
// BUG 4: off‑by‑one and assumes enemies is never null
for (int i = 0; i <= enemies.length; i++) {
difficulty += enemies[i] * (i + 1);
}
return difficulty;
}
}
Update GameApp.java
Add this method to the bottom of GameApp (inside the class, after printPlayerStats):
private static void demoLevels() {
System.out.println("=== Level Loader Demo ===");
String input = "level: 4"; // note the space before 4
System.out.println("Parsing input: \"" + input + "\"");
int level = InputParser.parseLevel(input);
System.out.println("Parsed level: " + level);
int[] enemies = LevelLoader.loadEnemiesForLevel(level);
int difficulty = LevelLoader.computeDifficulty(enemies);
System.out.println("Total difficulty for level " + level + ": " + difficulty);
}
Then, in main, after the mystery player block, add:
demoLevels();
Make sure GameApp.java now:
- Uses
Player,ScoreUtils,LevelLoader, andInputParser.
2. Run and observe
- Open
GameApp.java. - Click Run.
Expected symptoms:
- The scoreboard section should behave as in Level 3 (assuming you fixed it).
- The Level Loader demo will likely:
- Throw a
NumberFormatExceptionorNullPointerException. - Or print a strange difficulty.
- Throw a
Look at:
- The exception type.
- The stack trace: which method and line caused it?
3. Step through the layered flow
Use the debugger to trace the whole path:
- Set a breakpoint at the first line of
demoLevels. - Click Debug.
- When paused:
- Step Over the print statements.
- Use Step Into on
InputParser.parseLevel(input).
Inside parseLevel:
- Inspect
input,parts, andnumberPart. - Step line‑by‑line and watch for:
ArrayIndexOutOfBoundsExceptiononparts[1].NumberFormatExceptiononInteger.parseInt.
Once you understand the problem, stop debugging and fix parseLevel to:
- Validate the input format.
- Trim whitespace.
- Return a reasonable default (for example
1) or throw a clearer exception.
Run again with the debugger to confirm the parsing step works.
4. Follow the null / off‑by‑one bugs
Next, set a breakpoint on:
int[] enemies = LevelLoader.loadEnemiesForLevel(level);
- Debug again and Step Into
LevelLoader.loadEnemiesForLevel. - For the given
level, see which branch runs and what it returns. - Step back to
demoLevelsand then Step IntocomputeDifficulty.
Inside computeDifficulty:
- Watch the values of
i,enemies.length, anddifficulty. - See what happens when
iequalsenemies.length. - If
enemiesis null, notice how that changes things.
Fix the bugs by:
- Making
loadEnemiesForLevelreturn a non‑null array, even for unknown levels (for example, an emptyint[]). - Adjusting the
forloop incomputeDifficultyso it:- Does not go out of bounds.
- Handles
enemiesbeing null or empty safely.
Re‑run and confirm:
- No exceptions occur.
- The printed difficulty matches your expectations.
5. Avoid introducing new bugs
After each fix, re‑run the whole program, not just the Level Loader demo:
- Check that:
- The scoreboard output (from Level 3) still looks correct.
- No new exceptions appear.
This is a realistic part of debugging:
- Fix one bug.
- Run the full program.
- Make sure you didn’t break something else.
6. Reflection (Level 4)
Short prompts:
- What kinds of bugs did you see in this Level (compile‑time, runtime, logic)?
- How did stepping into helper methods (
parseLevel,loadEnemiesForLevel,computeDifficulty) help? - What habits can help avoid layered bugs (e.g., validating inputs, returning empty collections instead of null)?
You can compare your fixes to the suggested ones in Solutions & Fixes.
Solutions & Fixes
This section shows one set of possible fixes for the workshop exercises.
Your exact solutions may differ slightly as long as they:
- Remove the crashes.
- Produce correct behavior.
- Keep the code readable.
Level 1 – Reading Errors
BuggyCalculator.divide
One reasonable fix:
public static int divide(int a, int b) {
if (b == 0) {
// FIX: prevent division by zero and return a safe default
System.out.println("Cannot divide " + a + " by zero.");
return 0; // or throw new IllegalArgumentException("b must not be zero");
}
return a / b;
}
BuggyCalculator.sumFirstThree
Handle short arrays safely:
public static int sumFirstThree(int[] numbers) {
if (numbers == null || numbers.length == 0) {
// FIX: handle null or empty arrays without throwing an exception
return 0;
}
int limit = Math.min(3, numbers.length);
int total = 0;
for (int i = 0; i < limit; i++) {
// FIX: stop before we run past the end of the array
total += numbers[i];
}
return total;
}
BuggyLoop.countUpTo
If the intent is to count how many numbers from 1 to n (inclusive):
public static int countUpTo(int n) {
int count = 0;
for (int i = 1; i <= n; i++) {
// FIX: loop up to and including n so the count matches the intent
count++;
}
return count;
}
Level 2 – Debugger Basics
The key fixes here are the same as Level 1, but discovered using:
- Breakpoints.
- Stepping.
- Variable inspection.
See the Level 1 solutions above.
Level 3 – Multi‑File Debugging
ScoreUtils.averageScore
Correct the average calculation:
public static double averageScore(int[] scores) {
if (scores == null || scores.length == 0) {
// FIX: avoid dividing by zero and define a default average
return 0.0;
}
int total = 0;
for (int score : scores) {
total += score;
}
// FIX: divide by the correct length to get an accurate average
return (double) total / scores.length;
}
ScoreUtils.bestScore
Handle empty arrays and negative scores:
public static int bestScore(int[] scores) {
if (scores == null || scores.length == 0) {
// FIX: define behavior for empty or null score lists
return 0; // or throw an exception, depending on requirements
}
int best = scores[0];
for (int i = 1; i < scores.length; i++) {
// FIX: compare all elements starting from index 1
if (scores[i] > best) {
best = scores[i];
}
}
return best;
}
ScoreUtils.totalScore
Defensive null handling:
public static int totalScore(Player player) {
if (player == null || player.getScores() == null) {
// FIX: return a safe default when player or scores are missing
return 0;
}
int sum = 0;
for (int s : player.getScores()) {
sum += s;
}
return sum;
}
Level 4 – Layered Bugs
InputParser.parseLevel
A safer implementation:
public static int parseLevel(String input) {
if (input == null) {
// FIX: handle null input by falling back to level 1
return 1; // default level
}
String trimmed = input.trim(); // e.g., "level: 4"
String[] parts = trimmed.split(":");
if (parts.length < 2) {
// FIX: validate the basic "label:number" format
return 1; // or throw new IllegalArgumentException("Invalid level format");
}
String numberPart = parts[1].trim();
try {
// FIX: trim and safely parse the numeric part
return Integer.parseInt(numberPart);
} catch (NumberFormatException e) {
// FIX: use a safe default when parsing fails instead of crashing
return 1;
}
}
LevelLoader.loadEnemiesForLevel
Return a non‑null array:
public static int[] loadEnemiesForLevel(int level) {
if (level == 1) {
return new int[] {2, 3};
} else if (level == 2) {
return new int[] {4, 4, 5};
} else if (level == 3) {
return new int[] {10};
} else {
// FIX: return an empty array instead of null for unknown levels
return new int[0];
}
}
LevelLoader.computeDifficulty
Fix the off‑by‑one and null handling:
public static int computeDifficulty(int[] enemies) {
if (enemies == null || enemies.length == 0) {
// FIX: define a difficulty of 0 when there are no enemies
return 0;
}
int difficulty = 0;
for (int i = 0; i < enemies.length; i++) {
// FIX: stop at enemies.length - 1 to avoid going out of bounds
difficulty += enemies[i] * (i + 1);
}
return difficulty;
}
Putting it all together
After applying these fixes:
- The programs should compile and run without exceptions.
- The scoreboard should report sensible averages, best scores, and totals.
- The level loader should parse inputs, avoid null issues, and compute difficulty correctly.
If your results differ, use the debugger again to:
- Set breakpoints in the fixed methods.
- Step through and verify that variable values match your expectations.