Top 10 tips to debug your Java code with IntelliJ
In today’s fast-paced development world, debugging can easily become a dreaded task, so here is the complete guide to Debugging Java code in IntelliJ. You write what seems like perfect code, only to watch it fail mysteriously during runtime. Furthermore, maybe a NullPointerException crashes your app at the worst moment, or a complex bug hides in tangled logic, causing hours of frustration. Even with AI-powered coding assistants helping generate boilerplate, the need to understand and troubleshoot your code deeply has never been greater, especially when debugging Java code in IntelliJ.
For example, imagine spending a whole afternoon chasing an elusive bug that breaks customer workflows—only to realize it was a simple off-by-one error or a condition you never tested. This experience is all too real for developers, and mastering your debugging tools can mean the difference between headaches and smooth sailing when debugging Java code in IntelliJ.
That’s where IntelliJ IDEA’s powerful debugger steps in — it lets you pause execution, inspect variables, explore call stacks, and follow exactly what’s going wrong step by step. Whether you’re investigating a tricky edge case or validating AI-generated code, sharpening your IntelliJ debugging skills transforms guesswork into confidence.
This post will guide you through practical, hands-on tips to debug Java effectively with IntelliJ, ultimately turning one of the most daunting parts of development into your secret weapon for quality, speed, and sanity.
Why do we debug code?
When code behaves unexpectedly, running it isn’t enough — you need to inspect what’s happening at runtime. Debugging lets you:
- Pause execution at a chosen line and then inspect variables.
- Examine call stacks and then jump into functions.
- Evaluate expressions on the fly and then change values.
- Reproduce tricky bugs (race conditions, exceptions, bad input) with minimal trial-and-error.
Additionally, good debugging saves time and reduces guesswork. Moreover, it complements logging and tests: use logs for high-level tracing and debugging Java code in IntelliJ for interactive investigation.

Prerequisites for Debugging Java code in IntelliJ
- IntelliJ IDEA (Community or Ultimate). Screenshots and shortcuts below assume a modern IntelliJ release.
- JDK installed (e.g., Java 21 or whichever version your project targets).
- A runnable Java project in IntelliJ (Maven/Gradle or a simple Java application).
Key debugger features and how to use them
1. Breakpoints
A breakpoint stops program execution at a particular line so you can inspect the state.
- How to add a breakpoint: Click the gutter (left margin) next to a line number or press the toggle shortcut. The red dot indicates a breakpoint.
Breakpoint variants:
- Simple breakpoint: pause at a line.
- Conditional breakpoint: pause only when a boolean condition is true.
- Right-click a breakpoint → “More” or “Condition”, then enter an expression (e.g., numbers[i] == 40).
- Log message / Print to console: configure a breakpoint to log text instead of pausing (helpful when you want tracing without stopping).
- Method breakpoint: pause when a specific method is entered or exited (note: method breakpoints can be slower — use sparingly).
- Exception breakpoint: pause when a particular exception is thrown (e.g., NullPointerException). Add via Run → View Breakpoints (or Ctrl+Shift+F8) → Java Exception Breakpoint.
Example (conditional):
for (int i = 0; i < numbers.length; i++) {
System.out.println("Processing number: " + numbers[i]); // set breakpoint here with condition numbers[i]==40
}Expected behavior: the debugger pauses only when the evaluated condition is true.

2. Watchpoints (field watch)
A watchpoint suspends execution when a field is read or written. Use it to track when a shared/static/class-level field changes.
How to set:
- Right-click a field declaration → “Toggle Watchpoint” (or add in the Debug tool window under Watches).
- You can add conditions to watchpoints too (e.g., pause only when counter == 5).
Note: watchpoints work at the field level (class members). Local variables are visible in the Variables pane while stopped, but you can’t set a watchpoint on a local variable.
3. Exception breakpoints
If an exception is thrown anywhere, you may want the debugger to stop immediately where it originates.
How to set:
- Run → View Breakpoints (or Ctrl+Shift+F8) → + → Java Exception Breakpoint → choose exception(s) and whether to suspend on “Thrown” and/or “Uncaught”.
This is invaluable to find the exact place an exception is raised (instead of chasing stack traces).
Here’s an expanded and more practical version of those sections. It keeps your tone consistent and adds real-world examples, common use cases, and small code snippets where helpful.
4. Evaluate Expression & Watches (Practical usage)
While the debugger is paused, these tools help you experiment and verify behavior without editing code.
Evaluate Expression (Alt+F8 / ⌥F8)
You can run expressions in the current stack frame. This is useful for checking logic, testing small fixes, or calling helper methods.
Example:
int discount = priceCalculator.applyDiscount(originalPrice);While paused, evaluate:
priceCalculator.applyDiscount(500)This lets you confirm whether the logic works without rerunning the app.
You can also check null-safety:
user.getAddress().getCity()If this throws a NullPointerException inside Evaluate Expression, you instantly know which object is missing.
Modify runtime values:
You can temporarily change variables to see how the code behaves:
- i = 10
- currentUser = new User(“test-user”);
This helps you test alternate paths without restarting.
Watches
Watches let you track variables or expressions as you step.
Examples:
- Watching a filtered list
items.stream().filter(x -> x.isActive()).count()- Watching a computed value:
totalAmount * 1.18- Watching nested fields:
order.customer.address.cityWatches update on every step, so you can see how values evolve inside loops or during recursive calls.
Great when debugging loops:
If you add:
- numbers[i]
- i
you can quickly spot when a loop behaves incorrectly.
5. Stepping: Step Over / Into / Out / Smart Step Into (Practical usage)
Stepping helps you walk through logic precisely.
Step Over (F8)
Use when you want to execute the current line but don’t need to go inside the method call.
Example:
You trust validateUser() and only care about the next few lines:
- validateUser(user); // Step Over
- processPayment(user);
Step Into (F7)
Use when you need to inspect what’s happening inside a method.
Example:
You’re getting wrong totals:
double finalAmount = billingService.calculateTotal(cart);Step Into calculateTotal() to check discounts, taxes, and rounding errors.
Smart Step Into (Shift+F7)
Perfect when multiple method calls are on the same line:
String result = helper.clean(input.trim().toLowerCase());Smart Step Into lets you choose whether to enter trim(), toLowerCase(), or clean().
Step Out (Shift+F8)
If you accidentally stepped too deep into a method, Step Out returns you to the caller quickly.
6. Step Filtering (Practical usage)
When you step into code, IntelliJ might enter library methods you’re not interested in. Step Filtering avoids this.
To skip stepping into certain packages/classes (e.g., java.*, third-party libs):
Settings → Build, Execution, Deployment → Debugger → Stepping.
Add classes or package patterns (you can use com.mycompany.* or java.*).
Apply → OK.
7. HotSwap / Redeploy code during debugging (Practical usage)
HotSwap lets IntelliJ reload small code changes without restarting the app.
What you can change without restart:
- Method body logic
- Local variables
- Simple refactors within a method
Example:
You changed:
- return price * 0.9;
to:
- return price * 0.85;
Recompile, click “Reload classes,” and the debugger uses the updated logic immediately.
Great for:
- Tuning formulas
- Fixing off-by-one errors
- Tweaking conditions
- Adjusting log messages
Not supported:
- Adding new methods
- Adding fields
- Changing class hierarchy
For bigger changes you’ll need a restart.
8. Remote debugging (attach to JVM) (Practical usage)
Remote debugging helps when your app runs in environments like:
- Docker containers
- Staging servers
- Background JVMs
- Microservices
- Local processes started by another script
Example JVM arg for a Spring Boot app:
- -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
You can connect IntelliJ to port 5005 and debug as if the app were local.
Common use case:
Your REST API behaves differently inside Docker.
Attach debugger → Set breakpoints in your service → Reproduce the issue → Inspect environment-specific behavior.
9. Debugging unit tests (Practical usage)
Right-click a test and run in debug mode.
Useful for:
- Verifying mocks and stubbing
- Tracking unexpected NPEs inside tests
- Checking the correctness of assertions
- Understanding why a particular test is flaky
Example:
Your test fails:
assertEquals(100, service.calculateTotal(cart));Set a breakpoint inside calculateTotal() and run the test in debug mode.
You instantly see where values diverge.
10. Logs vs Breakpoints: when to use which (Practical usage)
Use both together depending on the situation.
Use logs when:
- You need a history of events.
- The issue happens only sometimes.
- You want long-term telemetry.
- It’s a production or staging environment.
Use breakpoints when:
- You need to inspect exact values at runtime
- You want to experiment with Evaluate Expression
- You want to track control flow step-by-step
Log Message Breakpoints (super useful)
These let you print useful info without editing code.
Example:
Instead of adding:
System.out.println("i = " + i);You can configure a breakpoint to log:
"Loop index: " + iand continue execution without stopping.
This is ideal for debugging loops or repeated method calls without cluttering code.

Example walkthrough (putting the pieces together)
- Open DebugExample.java in IntelliJ.
- Toggle a breakpoint at System.out.println(“Processing number: ” + numbers[i]);.
- Right-click breakpoint → add condition: numbers[i] == 40.
- Start debug (Shift+F9). Program runs and pauses when numbers[i] is 40.
- Inspect variables in the Variables pane, add a watch for i and for numbers[i].
- Use Evaluate Expression to compute numbers[i] * 2 or call helper methods.
- If you change a method body and compile, accept HotSwap when IntelliJ prompts to reload classes
Common pitfalls & tips
- Method/exception breakpoints can be slow if used everywhere — prefer line or conditional breakpoints for hotspots.
- Conditional expressions should be cheap; expensive conditions slow down program execution during debugging.
- Watchpoints are only for fields; for locals, use a breakpoint and the Variables pane.
- HotSwap is limited — don’t rely on it for structural changes.
- Remote debugging over public networks: Be careful exposing JDWP ports publicly — use SSH tunnels or secure networking.
- Avoid changing production behavior (don’t connect a debugger to critical production systems without safeguards).
Handy keyboard shortcuts (Windows/Linux | macOS)
- Toggle breakpoint: Ctrl+F8 | ⌘F8
- Start debug: Shift+F9 | Shift+F9
- Resume: F9 | F9
- Step Over: F8 | F8
- Step Into: F7 | F7
- Smart Step Into: Shift+F7 | Shift+F7
- Evaluate Expression: Alt+F8 | ⌥F8
- View Breakpoints dialog: Ctrl+Shift+F8 | ⌘⇧F8
(Shortcuts can be mapped differently if you use an alternate Keymap.)
Key Takeaways
- Debugging is essential because it helps you understand and fix unexpected behavior in your Java code beyond what logging or tests can reveal.
- IntelliJ IDEA offers powerful debugging tools like breakpoints, conditional breakpoints, watchpoints, and exception breakpoints, which allow you to pause and inspect your code precisely.
- Use features like Evaluate Expression and Watches to interactively test and verify your code’s logic while paused in the debugger.
- Stepping through code (Step Over, Step Into, Step Out) helps uncover issues by following program flow in detail.
- HotSwap allows quick code changes without restarting, therefore speeding up the debugging cycle.
- Remote debugging lets you troubleshoot apps running in containers, servers, or other environments thereby, enabling seamless investigation.
- Combine logs and breakpoints strategically depending on the situation, therefore, to maximize insight.
- Familiarize yourself with keyboard shortcuts and IntelliJ’s debugging settings ultimately, for an efficient workflow.
Conclusion
In fact, IntelliJ’s debugger is powerful — from simple line breakpoints to remote attachment, watches, exception breakpoints, and HotSwap. As a result, practicing these workflows will make you faster at diagnosing issues and understanding complex code paths. Debugging Java code in IntelliJ. Start small: set a couple of targeted conditional breakpoints, step through the logic, use Evaluate Expression, and gradually add more advanced techniques like remote debugging or thread inspection.
Click here to read more blogs like this.
An SDET with hands-on experience in the life science domain, including manual testing, functional testing, Jira, defect reporting, web application, and desktop
application testing. I also have extensive experience in web and desktop automation using Selenium, WebDriver, WinAppDriver, Playwright, Cypress, Java, JavaScript, Cucumber, maven, POM, Xray, and building frameworks.
