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.

Java programming emblem with a coffee cup, code braces, and terminal
Inside this lesson 10 sections 5 questions 5 figures

SECTION 1

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?

SECTION 2

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.

SECTION 3

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.

IDE Debugger LayoutCODE EDITOR5 int sum = 0;6 for (int i = 0; i < 5; i++) {7 sum += arr[i]; ← paused here8 }9 System.out.println(sum);● = Breakpoint (red dot in gutter)Highlighted line = Current execution pointVARIABLES PANELNameValuesum3i2arr{1, 2, 4, 8, 16}arr[i]4CALL STACK▶ main(String[] args) : line 7 (bottom of stack)DEBUG CONTROLS▶ ResumeStep OverStep IntoStep Out■ Stop
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.

SECTION 4

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.

SECTION 5

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.

Stepping Actions Flowchartmain() method10int x = 5;11int y = double(x);12System.out.println(y);double() method20static int double(int n) {21 return n * 2;22}From Line 11, paused at breakpoint:STEP OVER (F8)→ Jumps to line 12STEP INTO (F7)→ Enters double(), line 20STEP OUT (⇧F8)→ N/A if at top levelStep IntoStep Over skips methodVariable State After Each Step from Line 11Step Over:x=5, y=10 (line 12)Step Into:n=5 (inside double, line 20)
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.
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

Common debug stepping actions and their keyboard shortcuts (IntelliJ IDEA defaults)

SECTION 6

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. 1

    Step 1 — Set a Breakpoint

    Click 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. 2

    Step 2 — Launch in Debug Mode

    Click 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. 3

    Step 3 — Step Over to the Loop

    Press 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. 4

    Step 4 — Identify Bug #1

    The 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. 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; }

SECTION 7

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.

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

Comparison of IDE debugging vs. print-statement debugging

✦

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.

SECTION 8

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.

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

How basic debugging concepts extend into advanced features

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.

SECTION 9

Practice Problems

Question 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?
Show answer
When you click Run, the program executes from start to finish without stopping, ignoring any breakpoints you may have set. When you click Debug, the IDE launches the JVM in debug mode, which means the JVM and IDE communicate through the Java Debug Wire Protocol. If the program reaches a line with a breakpoint, it pauses execution so you can inspect variables, view the call stack, and step through subsequent lines. Without Debug mode, breakpoints are simply ignored and have no effect on the program's execution.

Question 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?
Show answer
When the debugger pauses at the breakpoint on line int b = a + 4;, that line has not yet executed. So the Variables panel shows a = 3, and b is not yet defined (or shows as 0 if it was previously declared). After pressing Step Over (F8) once, the line executes and the debugger pauses on int c = b * 2;. Now the Variables panel shows a = 3 and b = 7. The variable c is not yet computed.

Question 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?
Show answer
If you press Step Over (F8), the debugger executes the entire multiply(3, 4) method call in one step and pauses on the next line: System.out.println(result);. The Variables panel shows result = 12. If you press Step Into (F7), the debugger enters the multiply method and pauses at its first executable line: return x * y;. The Variables panel shows x = 3 and y = 4. The call stack would show multiply on top with main below it.

Question 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?
Show answer
Set a breakpoint on the line if (arr[i] > max) inside the loop. On the first iteration, the Variables panel shows i = 0, arr[0] = -5, max = 0. Since −5 is not greater than 0, the if-block is skipped. Stepping through all four iterations, you'd observe that max never changes because all values are negative and none is greater than 0. The bug is that max is initialized to 0 instead of arr[0] (or Integer.MIN_VALUE). The fix is to change the initialization to int max = arr[0]; and start the loop from i = 1. The debugger's variable inspection makes this obvious on the very first iteration.

Question 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).
Show answer
A simple breakpoint inside a loop that runs 10,000 times would pause execution on every single iteration, forcing you to press Resume thousands of times before reaching the problematic iteration. This is extremely impractical and time-consuming. A more efficient strategy involves using a conditional breakpoint, which only pauses when a specified condition is true. For example, if you suspect the bug occurs around iteration 5,000, you could set a conditional breakpoint with the condition i == 5000. Alternatively, if you know the variable should never exceed a certain value, you could set the condition to something like total < 0. This way, the program runs at full speed through all the unproblematic iterations and pauses precisely when the anomaly occurs. In IntelliJ, you can set a conditional breakpoint by right-clicking on an existing breakpoint and typing a Java boolean expression. This strategy combines the precision of the debugger with the efficiency of letting the program run freely when nothing is wrong.

SUMMARY

Lesson Summary

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)