A debugger pauses a running Java application so you can inspect its state and follow execution. It is most effective after you identify a reproducible input and the behavior you expected. This lesson investigates a concrete discount boundary rather than presenting debugging as random stepping through every line. You can use an IDE debugger or the JDK's jdb tool; the underlying questions are the same: which branch ran, what values entered it, and where did the actual result first diverge?
Define the failing case before opening the debugger
The sample pricing requirement says a quantity of ten or more receives a two-unit discount. A strict greater-than comparison incorrectly excludes ten. Record that boundary input, the expected total, and the actual total from the defective version. Nearby values such as nine and eleven help show that the mistake concerns the threshold rather than all multiplication. This evidence gives the debugger a specific question to answer.
Compile information useful for inspection
javac -g includes debugging information such as local variable details alongside line and source information. IDEs commonly arrange suitable compilation automatically, but check the build if local variables are unavailable. Start the debugger with the intended classpath and class name. Debugging an old class file while reading an edited source file can display confusing line positions and values, so rebuild successfully before investigating a changed version.
Set a breakpoint before the suspect decision
A breakpoint on the conditional pauses before the branch's effect. Inspect quantity, unitPrice, and the comparison result. A conditional breakpoint can stop only when quantity is ten, reducing noise when a larger application processes many requests. Do not change several variable values immediately; first observe the failure as it actually occurs. The purpose is to connect the wrong result to a specific decision, not to temporarily force the display to look correct.
Read the call stack and step intentionally
The call stack shows the current method and callers that led to it. Step into follows a called method when its behavior is uncertain. Step over is appropriate for a known helper you do not need to inspect. Step out returns to the caller, letting you compare the returned value with its expected contract. In recursive code, several frames may share a method name while holding different local variables; select the correct frame before interpreting a value.
Break on exceptions when the origin is unclear
An exception breakpoint can stop when an exception is thrown, even if a later catch handles it. This helps locate a NumberFormatException originating far from the final error message. Distinguish the throw site from the handling site and preserve the cause when exceptions are wrapped. A debugger expression may invoke code, so avoid evaluating a method that consumes input, updates a collection, or changes application state merely to inspect it.
Turn the discovery into a regression check
Once you explain the defect, make the smallest correct change and verify the original failing case. Add a JUnit test for the boundary and nearby cases, then run the relevant suite. A debugger session is temporary evidence; a regression test makes the requirement executable for later edits. Keep useful logs for failures that occur outside local debugging, but do not leave large amounts of diagnostic output in the product's normal console flow.
Practice tasks and checks
Temporarily change the comparison in DiscountBoundary to the intentional defective strict version and rebuild with -g. Run the program once to record the wrong middle output. In the debugger, stop before the comparison for quantity ten and inspect the boolean condition. Restore the inclusive comparison only after you can state why the branch was wrong. Then write tests for quantities nine, ten, and eleven using the exact expected totals. For a second investigation, pass an invalid number string to a parsing helper and use an exception breakpoint to locate the throw site. Keep the two investigations separate: one is a business-rule boundary defect, and the other is input validation.
Java Summary
- Inspect the first decision where actual behavior diverges from the requirement.
- Use breakpoints and stack frames to understand the relevant state.
- Record a successful fix with a regression test.
- Continue with testing foundations when these examples are clear.
Java Examples
Each example shows the code, the output it produces and what each part does.
Inspect a correct boundary rule
Save this program as DiscountBoundary.java. Compile it with javac DiscountBoundary.java and run it with java DiscountBoundary.
Expected output:
Nine units produce thirty-six with no discount. Ten units produce forty minus two, and eleven produce forty-four minus two. Put the breakpoint before the return expression and inspect the condition for each call. An intentional defective version would use quantity > 10 and incorrectly return forty for the middle case. The complete runnable example shows the corrected requirement, while that one-character variation supplies a controlled debugging exercise.
public class DiscountBoundary {
static int total(int quantity, int unitPrice) {
int gross = quantity * unitPrice;
return quantity >= 10 ? gross - 2 : gross;
}
public static void main(String[] args) {
System.out.println(total(9, 4));
System.out.println(total(10, 4));
System.out.println(total(11, 4));
}
}36
38
42Launch jdb with debugging information
Use the following shell commands or configuration.
What to expect: jdb opens an interactive prompt for DiscountBoundary; application output appears after run.
The first command rebuilds the class with local-variable debug information. The second launches the JDK debugger using the current directory as the classpath root. The debugger is interactive, so simply opening it does not mean main has completed. Use the session commands shown next to set a method breakpoint, start execution, inspect state, and continue. An IDE presents the same operations with buttons and variable panels.
javac -g DiscountBoundary.java
jdb -classpath . DiscountBoundaryInspect the paused method
Use the following text commands or configuration.
What to expect: The debugger stops in total, shows its current arguments and stack, then continues.
stop in names a method breakpoint. run starts the application. At the breakpoint, locals displays available local variables and arguments, while where shows the stack. cont resumes execution until another stop or completion. Exact prompt formatting varies by JDK, and locals can show different variables depending on the paused source position. Continue through all calls or exit the session when finished instead of leaving a paused process mistaken for a hung application.
stop in DiscountBoundary.total
run
locals
where
contJava FAQ
Why are local variables unavailable?
Check whether the class was compiled with appropriate debug information and whether the debugger is attached to the rebuilt class. The selected frame and paused position also affect which variables are currently in scope.
Should I fix a bug by changing a variable in the debugger?
Changing state can test a hypothesis, but it does not edit the program's rule. After observing the defect, correct source code, rebuild it, and verify the failing case with a lasting test.
What is a conditional breakpoint?
A breakpoint that only pauses when an expression you supply is true, such as quantity >= 10 or customerId.equals("A14"). It saves stepping through hundreds of loop passes to reach the one that misbehaves. In most IDEs you add the condition by right-clicking the breakpoint.
Can I debug a Java program running on another machine?
Yes, with remote debugging. Start the JVM with -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 and attach your IDE's debugger to that host and port. Only do this in development or test environments: an open debug port lets anyone who can reach it control the program.
How do I track down a NullPointerException quickly?
Read the message first: modern Java names the expression that was null. Then open the line at the top of the stack trace, or set an exception breakpoint for NullPointerException, and look at where that variable or field should have been given a value.
Java Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Which input distinguishes quantity > 10 from quantity >= 10?
What does the call stack show?
Java Quiz
2 questions to check what you learned.
Which command requests local-variable debug information?
- javac -g
- java -jar
- javadoc -quiet
What should remain after a successful debugging fix?
- A regression test for the failing behavior
- Only a screenshot of variables
- An unchanged defective comparison