Real-world examples of debugging Java applications in IntelliJ IDEA

If you write Java for a living, you spend a lot of time staring at stack traces and stepping through code. That’s where seeing real examples of debugging Java applications in IntelliJ IDEA becomes far more helpful than another dry feature list. Instead of just naming breakpoints and watches, this guide walks through concrete scenarios you actually hit in day‑to‑day work: null pointer crashes, slow endpoints, nasty concurrency bugs, and the “it only fails in production” mystery. We’ll walk through several examples of debugging Java applications in IntelliJ IDEA using the tools you already have installed: line and conditional breakpoints, smart step‑into, evaluate expression, the debugger console, and remote debugging. Along the way, you’ll see how to track down logic errors in Spring Boot controllers, fix off‑by‑one index issues, diagnose memory leaks, and understand multi‑threaded behavior. The goal is simple: after reading, you should be able to open IntelliJ, attach the debugger, and say, “I know exactly how to attack this bug.”
Written by
Jamie
Published

Hands-on examples of debugging Java applications in IntelliJ IDEA

Most tutorials start by listing debugger features. Let’s flip that. We’ll start with realistic examples of debugging Java applications in IntelliJ IDEA and highlight the tools as we use them.

Example 1: Hunting a NullPointerException in a Spring Boot endpoint

Scenario: A Spring Boot REST endpoint randomly throws a NullPointerException in production logs. Locally, it “works on my machine.”

How to attack this in IntelliJ IDEA:

You open the controller class and set a line breakpoint on the first line of the method handling the request. You reproduce the call using a tool like curl or Postman. When the debugger stops, you inspect parameters in the Variables view and use Evaluate Expression to check nested properties, such as user.getAddress().getZipCode().

The first run looks fine. To get closer to the real failure, you add a conditional breakpoint on the line that accesses user.getAddress() with a condition like:

user != null && user.getAddress() == null

Now the debugger only stops when the address is missing. You step over the code and see that a feature flag disables address loading for some users. That’s the real bug: the code assumes address is always present.

This is one of the best examples of debugging Java applications in IntelliJ IDEA because it shows how a simple conditional breakpoint can turn a flaky production issue into a deterministic repro.

Example 2: Off‑by‑one bug in a loop processing a list

Scenario: A nightly batch job fails with IndexOutOfBoundsException on a list of transactions.

You suspect an off‑by‑one error in a loop. Instead of adding System.out.println everywhere, you set a temporary breakpoint inside the loop on the line that reads from the list. You also add a watch on the list size and the loop index i.

On the next run, the debugger stops just before the crash. You see i equals transactions.size(), which is invalid for zero‑based indexing. Using Evaluate Expression, you quickly test what happens for i == transactions.size() - 1 and confirm that’s the last valid index.

You fix the loop condition from:

for (int i = 0; i <= transactions.size(); i++) {

to:

for (int i = 0; i < transactions.size(); i++) {

This example of debugging Java applications in IntelliJ IDEA shows how simple watches and temporary breakpoints beat print‑statement debugging every time.

Example 3: Slow Spring Boot endpoint using the profiler and method breakpoints

Scenario: An HTTP endpoint that used to respond in 50 ms now regularly takes 2–3 seconds.

You attach the debugger and use method breakpoints on suspicious service methods: UserService.loadUser, OrderService.calculateTotals, and DiscountService.applyDiscounts. Then you use Step Into and Smart Step Into to choose which method calls to follow.

You notice that DiscountService.applyDiscounts calls an external API inside a loop. With the debugger paused, you evaluate the size of the collection: cart.getItems().size() returns 120. That means 120 HTTP calls. No wonder it’s slow.

Switching to IntelliJ’s profiler (available in recent versions), you run a short load test and confirm that most time is spent in the HTTP client. Refactoring to batch the API calls fixes the issue.

Among the best examples of debugging Java applications in IntelliJ IDEA, this one highlights how combining method breakpoints, smart stepping, and profiling helps you find not just what is wrong, but where the time is actually going.

For more on performance profiling concepts in general (not IntelliJ‑specific), the University of Maryland has a helpful overview of software performance analysis techniques: https://www.cs.umd.edu/~pugh/IPS96/papers/chen.ps.

Example 4: Concurrency bug with stepping through multiple threads

Scenario: A scheduled job occasionally writes corrupted data. You suspect a race condition between two threads updating the same entity.

You enable “Stop on all breakpoints” and place breakpoints in both methods that write to the shared resource. In the Debug tool window, you switch to the Threads view and watch both threads as they hit their breakpoints.

Using Freeze/Resume Thread, you pause one thread right before it writes the data and let the other thread run. You inspect the shared object in both threads and see that one thread overwrites a field that the other just set.

This real example of debugging Java applications in IntelliJ IDEA shows why understanding the Threads panel matters. The ability to inspect variables per thread and control which threads run is the difference between guessing and actually seeing the race condition unfold.

If you want a deeper background on concurrency hazards, the classic lecture notes from Carnegie Mellon University on concurrent programming are worth a read: https://www.cs.cmu.edu/afs/cs.cmu.edu/project/pscico-guyb/realworld/www/lectures/Concurrency.pdf.

Example 5: Remote debugging a Java service in Kubernetes

Scenario: The bug only appears in your staging cluster behind an API gateway. Locally, everything behaves.

You configure your Java service with the standard JVM debug options, for example:


-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005

In IntelliJ IDEA, you create a Remote JVM Debug configuration pointing to the Kubernetes node or port‑forwarded service. Once attached, you can use the same breakpoints, watches, and evaluation tools as if the service were running on your laptop.

You set a breakpoint in the authentication filter and immediately see the difference: in staging, a reverse proxy adds extra headers that change the auth flow. You inspect request.getHeaders() and confirm the presence of a header that your local tests never set.

Remote debugging like this is one of the strongest real examples of debugging Java applications in IntelliJ IDEA because it closes the gap between “works on my machine” and “fails in the real environment.”

For secure remote debugging practices, it’s worth reading general guidance on securing remote access from NIST (National Institute of Standards and Technology): https://csrc.nist.gov/publications/detail/sp/800-46/rev-2/final.

Example 6: Memory leak investigation with heap analysis

Scenario: Your Java application slowly eats memory and eventually triggers OutOfMemoryError. Restarting helps, but only for a while.

You start the app in debug mode from IntelliJ IDEA and watch the Memory graph. As you exercise the application, you see a steady upward trend that never drops, even after garbage collection.

You trigger a heap dump (either from IntelliJ or using jmap) and open it in IntelliJ’s built‑in analyzer or an external tool. You sort by retained size and find a large number of ArrayList instances held by a static cache map.

Going back to the code, you set breakpoints on the cache’s put and remove methods. Stepping through, you discover that the remove path is never called for a certain user flow, so the cache grows forever.

This example of debugging Java applications in IntelliJ IDEA shows how the debugger and memory tools work together: the heap dump tells you what is leaking; the breakpoints and stepping show you why.

Example 7: Stepping through a complex lambda chain

Scenario: A data processing pipeline uses a long stream chain with multiple map, filter, and flatMap calls. The output is wrong, but the code is dense and hard to read.

You set a breakpoint inside the lambda passed to filter. With Smart Step Into, you choose exactly which lambda to enter, instead of stepping into every method call in the chain.

While stopped in the lambda, you inspect intermediate values with Evaluate Expression, trying out different predicates and mapping logic without restarting the program. You may, for example, run:

items.stream()
     .filter(i -> i.getPrice() > 100)
     .count()

right in the Evaluate dialog to check how many items match a condition.

This is one of the cleaner examples of debugging Java applications in IntelliJ IDEA that shows how Smart Step Into and Evaluate Expression make functional‑style code less opaque.

Example 8: Debugging tests with parameterized data

Scenario: A JUnit 5 parameterized test fails only for one specific input combination.

You run the test in debug mode and set a breakpoint at the top of the test method. In the Variables view, you see the current parameter values. You then add a conditional breakpoint that only triggers for the failing input, such as:

userType.equals("GUEST") && region.equals("EU")

Now you can run the entire test suite, but the debugger only stops when the problematic combination appears. Stepping through, you find a region‑specific tax rule that was never implemented for guests.

This is another practical example of debugging Java applications in IntelliJ IDEA: using conditional breakpoints in tests to zero in on the one data set that breaks your assumptions.

Patterns across these examples of debugging Java applications in IntelliJ IDEA

Across all these real examples of debugging Java applications in IntelliJ IDEA, a few patterns show up repeatedly:

  • Start with a breakpoint where the symptom appears, not where you think the cause is. Let the debugger show you reality.
  • Use conditional breakpoints to filter noise. They keep you from stopping on every iteration of a loop or every request.
  • Rely on Evaluate Expression instead of editing code. You can experiment with fixes or different inputs interactively.
  • Watch variables and fields that represent state over time. This is especially helpful for caches, sessions, and long‑lived services.
  • Use the Threads view for anything that smells like concurrency. If you’re debugging a deadlock or race, ignoring that panel is a mistake.
  • Combine runtime debugging with static analysis. IntelliJ’s inspections often point out potential NullPointerExceptions or concurrency issues before you even run the code.

These patterns turn IntelliJ from “just an IDE” into a solid debugging workbench, especially when you’re dealing with modern Java stacks: Spring Boot, Jakarta EE, microservices, and test‑heavy codebases.

Java development in 2024–2025 looks different from a few years ago, and that changes how you use the debugger:

  • More microservices and cloud‑native setups. Remote debugging through Docker and Kubernetes is no longer an edge case. The remote debugging example above is now routine.
  • Growing use of virtual threads (Project Loom). IntelliJ’s debugger has improved support for many lightweight threads, so the Threads view is even more important.
  • Heavier use of reactive frameworks. Debugging reactive pipelines can be tricky; Smart Step Into and method breakpoints on operators become lifesavers.
  • Shorter feedback cycles with CI/CD. Debugging locally and in pre‑production environments is part of the pipeline, not an afterthought.

Keeping these trends in mind, you can adapt the examples of debugging Java applications in IntelliJ IDEA here to newer frameworks and architectures without changing the core techniques.

For broader background on software testing and debugging practices that still hold up today, the classic material from MIT’s open courseware on software construction is useful context: https://ocw.mit.edu/courses/6-005-software-construction-spring-2016/pages/readings/.

FAQ: examples of debugging Java applications in IntelliJ IDEA

Q: Can you give a quick example of using conditional breakpoints in IntelliJ IDEA?
A: Yes. Suppose a loop processes 10,000 orders but only order with id == 1234 fails. Set a breakpoint inside the loop and add a condition order.getId() == 1234. IntelliJ will only stop when that condition is true, letting you inspect state for the failing case without stepping through every iteration.

Q: What are common examples of debugging Java applications in IntelliJ IDEA for beginners?
A: Beginners usually start with line breakpoints on methods that throw exceptions, stepping over code to see how variables change, and using Evaluate Expression to confirm simple logic. Fixing NullPointerExceptions, index errors, and basic logic bugs in if‑else branches are classic starter examples.

Q: How is remote debugging different from local debugging in IntelliJ IDEA?
A: The tools are the same—breakpoints, watches, stepping—but the app runs on a remote JVM (a server, container, or cluster node). You configure the JVM with debug options, create a Remote configuration in IntelliJ, and attach. It’s a direct way to apply the same examples of debugging Java applications in IntelliJ IDEA to staging or test environments.

Q: Is it better to use logging or the IntelliJ debugger?
A: You need both. Logging gives you a historical record in production. The debugger gives you an interactive view while the program runs. Many of the best examples of debugging Java applications in IntelliJ IDEA start with logs to locate the problem area, then switch to the debugger to inspect state and test hypotheses.

Q: Can IntelliJ IDEA help with debugging performance issues, not just errors?
A: Yes. You can combine method breakpoints, the built‑in profiler, and CPU snapshots to see where time is spent. The slow endpoint example above is a clear example of using IntelliJ to track down performance bottlenecks, not just crashes or exceptions.

Explore More Debugging Frameworks and Tools

Discover more examples and insights in this category.

View All Debugging Frameworks and Tools