JAVA • DEBUGGING AND TESTING

IDE Debugging — Use IDE debugging concepts (breakpoints, step, inspect variables) (intro)

Learn to pause, step through, and inspect your Java programs to find and fix bugs like a pro.

Historical Context & Motivation

In the early days of computing, finding errors in a program was a tedious and manual process. Programmers would insert print statements throughout their code to display variable values at various points, then sift through mountains of output to locate a mistake. This approach was slow, error-prone, and often introduced new bugs when the print statements themselves were incorrect. The need for a better way to examine a running program led to the development of interactive debuggers — tools that let you pause a program mid-execution and look inside.

1947
The First "Bug"
Grace Hopper's team famously found a moth stuck in a relay of the Harvard Mark II computer. The term debugging became a metaphor for fixing program errors.
1970s
Command-Line Debuggers
Tools like GDB (GNU Debugger) allowed programmers to set breakpoints and step through compiled C programs using text commands, but they required memorizing complex syntax.
1995
Java and JDB
When Java launched, it included JDB — a simple command-line debugger. The Java Platform Debugger Architecture (JPDA) provided a standard way for tools to control Java programs.
2001–Present
Modern IDE Debuggers
IDEs like IntelliJ IDEA, Eclipse, and VS Code integrated graphical debuggers directly into the editor, making breakpoints, stepping, and variable inspection as easy as clicking a button.

Today's Java developers rely on IDE debuggers daily. Instead of guessing where a bug might be, you can freeze your program at any line, examine every variable, and step forward one instruction at a time. The central question this lesson addresses is: how do breakpoints, stepping, and variable inspection work together to help you find and fix bugs efficiently?

Core Principles & Definitions

Before diving into hands-on examples, it helps to understand the three foundational concepts that make IDE debugging possible. Each concept builds on the one before it: you first tell the program where to stop, then you control how it moves forward, and finally you look at what the program knows at each moment.

1

Breakpoints

A breakpoint is a marker you place on a specific line of code. When the program reaches that line during execution, it pauses automatically so you can examine its state. You can set as many breakpoints as you need.
2

Stepping (Step Over, Step Into, Step Out)

Once paused, stepping lets you advance through code one line (or one method call) at a time. Step Over executes the current line. Step Into enters a method call. Step Out finishes the current method and returns to the caller.
3

Inspect Variables

While paused, the IDE shows you the current values of all local variables, object fields, and method parameters. You can also hover over any variable in the editor to see its value, or add watch expressions to track specific values as you step.
4

The Call Stack

The call stack panel shows you the chain of method calls that led to the current pause point. This is like a trail of breadcrumbs — you can click any level to see where the call originated and what variables existed there.
✦ KEY TAKEAWAY
Think of debugging like pausing a movie. A breakpoint is the pause button — it freezes the action at a scene you want to examine. Stepping is the frame-by-frame advance button, letting you move forward one moment at a time. And inspecting variables is like zooming into the frame to read every detail — what each character is holding, what the scoreboard says, or what time the clock reads.

Visual Explanation — The Debugger Interface

The diagram below shows a simplified view of a typical IDE debugger layout when a Java program is paused at a breakpoint. Understanding this layout is essential because you will interact with these panels every time you debug.

A simplified IDE debugger layout. The red dots in the code editor's gutter represent breakpoints. The highlighted line shows where execution is currently paused. The Variables panel displays current values. The debug controls let you resume, step, or stop execution.

Notice how the four panels work together. The code editor tells you where the program is paused. The variables panel tells you what each variable currently holds. The call stack tells you how the program got to this point. And the debug controls let you decide what happens next. Together, they give you X-ray vision into your running program.

How the Debugger Works Under the Hood

You don't need to understand every low-level detail, but knowing the basics of how a debugger talks to your running Java program will deepen your confidence. When you click the Debug button (instead of the normal Run button), the IDE launches the Java Virtual Machine (JVM) in a special debug mode. The IDE and the JVM communicate through the Java Debug Wire Protocol (JDWP), which allows the IDE to send commands like "pause at line 7" or "tell me the value of variable sum."

The Debug Lifecycle

  1. Set breakpoints — You click in the gutter next to a line number. The IDE records that line and, when you start debugging, tells the JVM to watch for it.
  2. Launch in Debug mode — The JVM starts your program but listens for commands from the IDE over JDWP.
  3. Hit a breakpoint — When execution reaches a breakpoint line, the JVM suspends the thread and notifies the IDE. The IDE highlights the line and populates the variables panel.
  4. Step and inspect — Each time you click Step Over, the JVM executes one statement, then suspends again. The IDE refreshes all panels with updated data.
  5. Resume or stop — Clicking Resume lets the program run freely until it hits another breakpoint or finishes. Clicking Stop terminates the JVM.

Step Over vs. Step Into vs. Step Out

These three stepping modes are the most important controls to master. Step Over executes the entire current line, including any method calls on that line, and pauses on the next line. Use it when you trust that a method works correctly and just want to see its result. Step Into enters the method being called on the current line, pausing at the first line inside that method. Use it when you suspect the bug is inside the method. Step Out finishes executing the rest of the current method and pauses at the line that called it. Use it when you've stepped into a method and seen enough — you want to go back up.

💡 Pro Tip
If you accidentally Step Into a Java library method like System.out.println(), don't panic! Just click Step Out to return to your own code immediately.

Stepping Through Code — A Visual Flowchart

To really understand how stepping works, let's trace through a small Java program visually. The diagram below shows how the debugger moves through a method call when you choose different stepping actions.

This flowchart compares the three stepping actions. When paused at line 11 (which calls double(x)), Step Over runs the entire method and lands on line 12. Step Into enters the method body at line 20. Step Out would be used once inside double() to return to the caller.
Common debug stepping actions and their keyboard shortcuts (IntelliJ IDEA defaults)
ActionKeyboard ShortcutWhat It DoesWhen to Use
Step OverF8Executes the current line entirely, then pauses on the next lineWhen you trust the method on this line works correctly
Step IntoF7Enters the method being called on this line, pauses at its first lineWhen you suspect the bug is inside a method
Step OutShift+F8Finishes the current method and pauses at the caller's next lineWhen you've seen enough inside a method and want to go back up
ResumeF9Continues execution until the next breakpoint or program endWhen you want to jump ahead to another breakpoint

Worked Example — Finding a Bug with the Debugger

Let's walk through a realistic debugging session. Suppose you wrote a method to calculate the average of an integer array, but it's returning the wrong answer. Here's the buggy code:

🐛 Buggy Code
public static double average(int[] nums) { int sum = 0; for (int i = 1; i <= nums.length; i++) { sum += nums[i]; } return sum / nums.length; } Calling average(new int[]{10, 20, 30}) should return 20.0 but instead throws an ArrayIndexOutOfBoundsException.
Debugging Session: Finding the Off-by-One Error
1
Step 1 — Set a BreakpointClick in the gutter next to the line int sum = 0; to place a breakpoint there. This ensures the program pauses right when the method starts, before any calculations happen.
Red dot appears next to the line.
2
Step 2 — Launch in Debug ModeClick the Debug button (the bug icon) instead of the normal Run button. The program starts and immediately pauses at your breakpoint.
Variables panel shows: nums = {10, 20, 30}
3
Step 3 — Step Over to the LoopPress F8 (Step Over) once to execute int sum = 0;. The variables panel now shows sum = 0. Press F8 again to enter the for-loop. Check the variables panel.
Variables: sum = 0, i = 1 — Wait, i starts at 1, not 0!
4
Step 4 — Identify Bug #1The loop initializes i = 1 instead of i = 0. This means it skips nums[0] (the value 10). Also, the condition i <= nums.length means it tries to access nums[3], which doesn't exist — that's what causes the exception.
Bug found: Off-by-one error in loop bounds. Fix: change to i = 0; i < nums.length
5
Step 5 — Identify Bug #2 (Integer Division)After fixing the loop and re-running, Step Over past the loop. The variables panel shows sum = 60. Step Over the return statement. The returned value shows 20 (an int), not 20.0. Since both sum and nums.length are ints, Java performs integer division. For arrays like {10, 20, 33}, this would truncate the result.
Bug found: Integer division loses precision. Fix: change to (double) sum / nums.length
✅ Fixed Code
public static double average(int[] nums) { int sum = 0; for (int i = 0; i < nums.length; i++) { sum += nums[i]; } return (double) sum / nums.length; }

Debugger vs. Print Statements — Strengths & Limitations

Many beginning Java programmers rely on System.out.println() to debug their code. This approach works in a pinch, but it has significant disadvantages compared to using a proper debugger. On the other hand, there are a few situations where print statements might still be useful. Let's compare both approaches directly.

Comparison of IDE debugging vs. print-statement debugging
FeatureIDE DebuggerPrint Statements
Setup effortClick to set a breakpoint — doneWrite, format, and place print lines manually
Variable inspectionAll variables visible automatically, including objects and arraysOnly the variables you specifically print
Code modificationNo code changes neededMust add and later remove print lines — risk of forgetting
Execution controlPause, step, resume at willNo control — program runs to completion
Works everywhereRequires an IDE with debug supportWorks in any environment, even a basic text editor
Multi-threaded programsCan pause individual threadsOutput from threads may interleave, creating confusion
✦ KEY TAKEAWAY
Using print statements to debug is like trying to diagnose a car engine problem by listening from outside. A debugger is like popping the hood, slowing the engine to one revolution at a time, and watching exactly what each component does. Both give you information, but the debugger gives you far more detail with far less effort.

Looking Ahead — Advanced Debugging Features

The breakpoint, step, and inspect workflow you've learned is the foundation of debugging, but modern IDEs offer powerful advanced features that build on these basics. As your Java programs grow more complex — with multiple classes, threads, and data structures — these tools become increasingly valuable.

How basic debugging concepts extend into advanced features
Basic Feature (This Lesson)Advanced Feature (Next Steps)
Simple breakpoint (always pauses)Conditional breakpoint — pauses only when a condition is true, e.g., i == 99
View variables in panelWatch expressions — evaluate custom expressions like arr.length - i
Step through one method at a timeEvaluate expression — run arbitrary Java code while paused to test hypotheses
Debug a single-threaded programThread debugging — pause and inspect individual threads in multi-threaded applications
Line breakpointsException breakpoints — automatically pause whenever a specific type of exception is thrown, even without a line breakpoint

For now, focus on mastering the core workflow: set a breakpoint, step through the code, and inspect variables. Once this becomes second nature, the advanced features will feel like natural extensions of what you already know.

Practice Problems

PROBLEM 1 — CONCEPTUAL
Explain the difference between clicking the Run button and clicking the Debug button in an IDE. What happens differently when the program reaches a breakpoint in each mode?
PROBLEM 2 — BASIC CALCULATION
Consider this code with a breakpoint on the line marked with // BP: int a = 3; int b = a + 4; // BP int c = b * 2; When the debugger pauses at the breakpoint, what values do you see in the Variables panel for a and b? What about after pressing Step Over once?
PROBLEM 3 — INTERMEDIATE
You are debugging the following code and are paused at the line marked // BP: public static void main(String[] args) { int result = multiply(3, 4); // BP System.out.println(result); } static int multiply(int x, int y) { return x * y; } Describe what happens differently if you press Step Over (F8) versus Step Into (F7) at this breakpoint. In each case, what line is highlighted next, and what variables are visible?
PROBLEM 4 — APPLIED
A classmate wrote this method to find the largest value in an array, but it returns the wrong answer for {-5, -2, -8, -1}: static int findMax(int[] arr) { int max = 0; for (int i = 0; i < arr.length; i++) { if (arr[i] > max) { max = arr[i]; } } return max; } Describe a debugging strategy: where would you set a breakpoint, what would you step through, and what would the variables panel reveal that points to the bug?
PROBLEM 5 — CRITICAL THINKING
Imagine you have a program with 500 lines of code and a loop that runs 10,000 times. Somewhere during the loop, a variable gets an incorrect value, but you don't know which iteration causes the problem. Explain why a simple breakpoint inside the loop would be impractical, and propose a more efficient debugging strategy using what you've learned (or what you might research about conditional breakpoints).

Lesson Summary

IDE debugging transforms the process of finding bugs from guesswork into a precise, controlled investigation. The three core tools are breakpoints (markers that pause execution at a specific line), stepping (advancing through code one line or one method at a time using Step Over, Step Into, and Step Out), and variable inspection (viewing the current values of all variables, parameters, and object fields while the program is paused).

When you debug in an IDE, you click the Debug button to launch the JVM in debug mode. The call stack panel shows the chain of method calls leading to the current line. Compared to print-statement debugging, IDE debuggers require no code changes, show all variables automatically, and give you full control over execution flow. As you advance, you'll encounter conditional breakpoints, watch expressions, and exception breakpoints — all of which build naturally on the foundational workflow you've learned today.

Varsity Tutors • Java • IDE Debugging — Use IDE debugging concepts (breakpoints, step, inspect variables) (intro)