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:
Main.java– entry point and helpers (printPlayerStats).Player.java– represents a player and their scores.ScoreUtils.java– helper methods.
OnlineGDB expects the class with main to be in Main.java with public class Main. Use a new OnlineGDB Java project for Levels 3–4 so you do not have two main methods in one project.
1. Setup the files
Create a new OnlineGDB Java project for Level 3. Add 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 {
public static double averageScore(int[] scores) {
int scoreLength = (scores == null) ? 0 : scores.length;
int total = 0;
for (int i = 0; i < scoreLength; i++) {
total += scores[i];
}
return (double) total / (scores.length + 1);
}
public static int bestScore(int[] scores) {
int scoreLength = (scores == null) ? 0 : scores.length;
int best = 0;
for (int score : scores) {
if (score > best) {
best = score;
}
}
return best;
}
public static int totalScore(Player player) {
int[] arr = player.getScores();
int scoreLength = (arr == null) ? 0 : arr.length;
int sum = 0;
for (int s : arr) {
sum += s;
}
return sum;
}
}
Main.java
public class Main {
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[] {});
System.out.println("=== Scoreboard ===");
printPlayerStats(alice);
printPlayerStats(bob);
printPlayerStats(charlie);
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();
int scoreLength = (scores == null) ? 0 : scores.length;
double avg = ScoreUtils.averageScore(scores);
int best = ScoreUtils.bestScore(scores);
int lineTotal = ScoreUtils.totalScore(player);
System.out.println("Player: " + player.getName());
System.out.println(" Average: " + avg);
System.out.println(" Best: " + best);
System.out.println(" Total: " + lineTotal);
System.out.println();
}
}
Make sure:
- All files are saved.
- Only
Main.javacontainspublic static void main.
2. Run and observe
- Open
Main.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
maininMain. - Click Debug, then the green Start button.
- 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. - In the Variables panel, watch
scoreLength,total, and the return value (OnlineGDB does not show array contents reliably).
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. - Click Debug, then the green Start button.
- Click Continue exactly 3 times so execution moves past the three
printPlayerStatscalls and stops on themysteryline. - When stopped there, click Step Into to enter
ScoreUtils.totalScore. - Identify the exact line where the exception occurs, then 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
scoreLengthand howbestchanges 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)
- 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.