The creators of IntelliJ IDEA claim that it’s the most intelligent Java IDE out there. IntelliJ IDEA is said to be obsessed with helping developers achieve their best and enjoy their work – consistently, sustainably, over time. There are 1000s of IDEA users who would vouch for that claim and who use IDEA as their preferred IDE rather than NetBeans, Eclipse or any of the other more well known IDE.
Manning recently released the book “IntelliJ IDEA In Action” and the following is an excerpt from the book. It deals with the different kind of errors encountered during development and how best to use IntelliJ IDEA to debug your Java J2EE applications. Although some portions of the excerpt are IntelliJ specific, there’s a lot of info about debugging techniques and best practices, which could be useful irrespective of which IDE you use.
from: IntelliJ IDEA in Action,
written by Fields, Saunders
Reprinted by permission of
Manning Publications Co.
programs under the IDEA debugger
breakpoints and monitoring runtime values
with threads and the call stack
all like to think that we never make mistakes. No matter how good a
developer you are, if you write programs then you also write
bugs—it’s an unavoidable fact of life. The question isn’t
whether you’ll need to debug your program, but how you plan on
going about doing it. Many beginning programmers rely on System.out.println() to show them what’s happening inside their program, but more
sophisticated developers understand the benefits of using a debugging
tool. The debugger included with IDEA is a powerful, easy-to-use tool for examining a running program to
determine what’s happening (or not happening) behind the
scenes. After reading this chapter, you’ll never need to rely
on print statements again.
this section, we’ll introduce the basic concepts of using a
symbolic debugger to analyze Java programs. If you’re familiar
with using such tools, and you just want to understand how the IDEA debugger works, feel free to skim ahead to the next section.
and fixing bugs with a debugger
must start with some bad news: A debugger can neither find nor fix
bugs. If it could, we’d all have a lot more free time. No,
you’ll need to find the bugs on your own, either through
testing or pure luck (if you call getting big nasty stack traces
lucky). Although the debugger won’t fix bugs for you, it’s
immensely helpful in tracking them down and understanding what you
must do to fix them.
A debugger is essentially a tool that can tell you what’s
going on inside the Java VM as your program is executing. It lets you start your program and
then walk through it, method by method or line by line, using a
process known as stepping through the code. Executing your
code this way lets you slow things down and monitor your
application’s progress. Each step of the way, you can examine
the state and contents of your application’s variables and
object references. The process can answer burning questions like,
“What is the value of the variable x at the start of this method?”, “Where did that null
pointer exception come from?”, and “How did I get here?”
you suspect a bug is lurking in your code, it’s time to bring
out the debugger. Before you begin hunting down bugs, it’s
important to know exactly what you’re looking for and to have
an idea where you may find it. In the wild, you’ll encounter
several different species of bugs, and the debugger can help you
track down all of them.
errors are the bugs you’re probably most familiar with. We bet
the first program you ever wrote consisted primarily of syntax
errors. (Heck, we still work with some guys like that.) Syntax
errors are invalid statements or code structure that the Java
compiler can’t make sense of. In other words, they’re
just plain wrong.
of syntax errors include referencing a variable that doesn’t
exist or passing the wrong number of parameters to a method. Thanks
to the real-time syntax analysis feature of IDEA’s
editor, you’ll probably catch most of these errors before they
get to the compiler, but occasionally one will slip through.
of this type surface during compilation and therefore can’t
affect running code. The debugger is designed for analyzing programs
that run, and it can’t help fix compilation problems like
syntax errors—you’re on your own for that. It will,
however, help determine the cause of any runtime errors you
errors stick out like a sore thumb; you can’t miss them. They
can rear their ugly heads at any time, often with dramatic explosions
of stack traces and cryptic error messages. The JVM throws a runtime error when something unexpected happens, like trying
to read off the end of an array or encountering a null where an
object was expected. In Java, these issues are called exceptions,
and they come in many varieties. Some exceptions you’ll handle
yourself; others will bubble up and be handled by the VM or your application server. (Of course, its idea of handling an
exception may be to shut down the application.)
you’ve encountered a runtime error, you can use IDEA’s
debugger to investigate its cause. If you have access to a stack
trace or log message for the exception, it may help narrow down the
source of the problem, possibly even giving the exact line number.
But as we said, exceptions are inherently unexpected—so the
question is, what happened that wasn’t expected? By executing
the program in the debugger, you can follow its progress, test your
assumptions, and observe the program data in a state of suspended
animation. All of these features should help you determine what went
bugs are trickier beasts because they may not be readily apparent.
With a logic error, your application seems to work fine, but the
outcome isn’t as expected. For example, if your new sorting
method generates lists of data that aren’t properly sorted, you
probably have a logic error.
runtime errors, the key to resolving logic errors is being able to
peek under the covers of the running program. By examining the
contents of your supposedly sorted list at each step of execution,
you can try to determine the issue at hand.
A race condition is a bug caused by an unexpected dependency on
the relative timing of events. For example, if an instance variable
is accessible by two threads simultaneously, one thread may interfere
with another’s use of the class. While this situation may not
cause a problem every time you run your application, the potential is
always there. Bugs tied to race conditions only cause a problem when
the timing is just right (or just wrong, depending how you look at
it). Race conditions are often the result of threading problems; to
help you find them, IDEA gives you the ability to examine all of your application’s
threads when searching for the problem.
with particle physics, the attempt to examine a system tends to alter
its behavior in some way. Heisenbugs, a take-off on the
Heisenberg uncertainty principle, are bugs that seemingly disappear
or change their appearance when you try to track them down in the
debugger. Although using a debugger is for the most part a
noninvasive process, be aware that it can affect your running code in
terms of timing and concurrency. In particular, applications will run
slower under the debugger, which may cause bugs to run for cover when
you try to seek them out. Race conditions and threading-related bugs
are particularly susceptible to this effect.
your code for debugging
nice thing about working with a debugger to root out problems is that
you aren’t required to modify your source code, recompile, use
a special VM,
implement a magic interface, or write your code in any particular
style. The debugger takes advantage of monitoring technology built
into the Java platform. You do, however, have to follow a few rules
to make sure that the debugger can operate correctly with your
symbolic debugging information
you build your application, the compiler converts all your pretty
Java code into a tight bundle of Java byte code. A debugger works by
tracking the byte code’s execution and reporting on the
original source code references that generated it. This relationship
between byte code, your code symbols, and their original source files
and line numbers is called symbolic debugging information, and
it isn’t strictly required by the JVM to run your program. After all, the JVM doesn’t care that line number 3,310 from the file
LogonEventListener.java added 1 to the value of the variable logins;
it just executes the byte code to make it happen.
you do care—unless you speak byte code. That is why when you
compile your application, you must tell the compiler to preserve
this information and save it as part of the generated class file.
Fortunately, this is the default mode in most compilers, but you
must be aware of this fact when specifying your compilation options.
Why would you ask the compiler to omit this information? Because
doing so reduces the size of your class files and may help protect
the class files from reverse engineering. For this reason, some
developers choose to compile their application twice, creating a
debug version (which they keep) and a production version (which they
deploy). For the production version, they omit the symbolic
debugging information for the reasons stated earlier.
the latest version of your code
that the debugger works by mapping the running byte code to the
original source code, it stands to reason that the two must be in
sync. If your source code has been modified since you compiled and
executed your application, then the debugger is unable to accurately
report on its progress. When lines of executing source don’t
match up to your local source, things can get very confused, very
quickly. We’ve all been caught watching the debugger step
through yesterday’s class with today’s source. For that
reason, it’s best to always perform a clean build of your
application before firing up the debugger, to make sure everything is
third-party source code
you’re using any third-party libraries and have their source
code, it’s a good idea to add it to your project. Sometimes
during your debugging you’ll want to follow the program flow
into these libraries to understand where a problem is occurring.
(Maybe it’s a bug in a vendor library and not your fault!) If
you have access to all the source code, you’ll more easily be
able to tell what’s going on.
just in time compiler (JIT)
is a component in the VM that helps improve the performance of running applications. It works
by caching generated machine code in memory and reusing it to speed
up execution of the program. When doing so, however, you lose your
ability to generate meaningful stack traces, even though the debugger
may still work. If a problem arises in executing the cached machine
code, the stack trace will only display the unhelpful reference in
you may want to turn off the JIT while debugging in order to more clearly follow what’s
happening in the case of a problem. Options vary by VM,
but generally you can disable the JIT by launching the VM with the parameter compiler=none. JDK 1.4 is smart about this type of thing, and it makes sure methods
with breakpoints in them aren’t optimized by the JIT;
this is supposed to eliminate this problem in the debugger.
you launch your program in the debugger, it runs uninterrupted until
it hits a breakpoint marker. A breakpoint is a logical code
reference that causes the debugger to suspend the program (or take
some other action) when it reaches that point. Once suspended, you
can use the debugger’s tools to examine the state of your
program. You can then advance the program to the next breakpoint or
the next line of code at your leisure.
you’ll set up breakpoints in your code to suspend the VM just as your program enters a section of code that you suspect is
the source of a bug. Then you can follow the program step by step
and make sure things are going as expected.
are markers known only to IDEA and the VM.
They aren’t saved in your Java source or built into your
generated class files. Therefore, breakpoints can be set or cleared
as needed, at any time—even while the program is running. You
may need to set them before launching your program, for example,
should you wish to stop its execution early. Conversely, you may
wish to toggle breakpoints on and off throughout the debugging
session as you try to narrow down the problem.
your source code
your code is compiled and ready to go, you launch the application in
the debugger to begin your bug hunting at the first breakpoint. From
there, you can peek at what’s happening inside your
application. We’ll discuss the details of using the debugger in
the next section of this chapter.
your application in the debugger
you use IDEA’s
debugger to launch your program, you’re actually running your
program with the VM’s
debug mode enabled. In debug mode, the VM continually reports information about the state of your program to
the debugger, which in turn displays the data to you in a meaningful
IDEA uses the Java Platform Debugger Architecture (JPDA),
a technology built into the Java2 platform, as a source of debugging
information. If you’re developing an application using Java 1.1
or earlier, you won’t be able to use the debugger.
through your program
debugger allows you to step through each line of source code at
whatever pace you desire. This lets you follow each step logically,
evaluating the results and observing program flow. You can choose to
examine every line as it’s executed or skip ahead to meaningful
parts of the code you wish to observe more closely.
the values of variables and program state
you step through the program, you have the opportunity to examine the
internal values of your classes to see if they contain what you
expect them to. You can peek inside collections and arrays, evaluate
the output of methods, and examine instance variables. The debugger
also presents information about the current state of execution, the
call stack, and your program’s threads in order to help you
understand exactly what your program is doing.
data values on the fly to observe the results of potential changes
strict observational capabilities, the debugger also lets you tweak
values on the fly in order to observe the results. For example, you
can force a method to return false instead of true, and observe the
outcome; or skip to the end of a loop by incrementing its counter.
Breakpoints are source code markers used to trigger actions during a debugging
session. Typically, the purpose behind setting a breakpoint is to
suspend program execution to allow you to examine program data.
However, IDEA can use breakpoints as triggers for a variety of different actions,
as you’ll learn in this section. Breakpoints can be set at any
time during the debugging process, including prior to launching your
program in the debugger. Your breakpoints won’t affect your
Java source code files directly, but breakpoints and their settings
are saved with your IDEA project so you can reuse them across debugging sessions.
can manage breakpoints through the Breakpoints panel, shown in
6.1, which is accessible through the menu option Run | View
Breakpoints (Ctrl+Shift+F8) as well as through the
corresponding icon on the Debug window toolbar.
with different types of breakpoints
IDEA lets you create four types of breakpoints. Each is managed through
its own tab in the Breakpoints panel. The number beside each
type of breakpoint indicates the number of breakpoints defined for
that type. IDEA supports the following types of breakpoints:
breakpoints are assigned to a particular line of Java source.
breakpoints act in response to the program entering or exiting a
breakpoints are triggered when a specified exception is thrown.
watchpoints allow you to react to any access or modification of
specific instance variables.
of these breakpoints addresses different debugging needs and has its
own individual settings. In this section, we’ll address some of
the concepts common to all types of breakpoints. We’ll discuss
each type of breakpoint in detail in the following sections.
easiest way to create line breakpoints in your application code is to
click the gutter area next to the line of interest. If you Alt+Click the gutter area, the appropriate breakpoint type will appear. Its
type depends on the context of the current line. You can remove a
breakpoint by clicking its icon in the gutter area . The keyboard
shortcut for toggling breakpoints is Ctrl+F8.
another gem for 5.0 that makes it easy to set up logging
breakpoints. Select an expression or symbol you want to log during
debugging, and then Shift-Click in the gutter next to the line
where you wish to set the breakpoint. Doing so automatically creates
a breakpoint that doesn’t suspend the VM but only logs the value of the expression to the debugging console.
back to the breakpoint’s source
method and line number breakpoints, you can use the View Source and Go to buttons to visit the source code the breakpoint
references. The View Source button loads the source into the
editor, and the Go to button also closes the Breakpoints panel and switches focus to the editor.
a breakpoint is set, the editor displays a breakpoint icon in the
gutter area to the left of the affected source code. Each type of
breakpoint is represented by its own icon in the editor, as shown in
table 6.1. The icons indicate both the type and status of the
breakpoint. You can place your mouse pointer over a breakpoint icon
in the gutter area of the editor to review the breakpoint’s
settings, including information about its type, location, and action.
In some cases, it’s possible for multiple breakpoints to apply
to the same line, in which case the icons line up next to one
another, expanding the gutter area as required.
icons also serve as convenient shortcuts for managing your
breakpoints. Clicking the icon removes the breakpoint. Successive
use of Alt+Click the icon toggles its state between enabled
and disabled. Right-clicking the breakpoint icon provides a context
menu with three options:
Disable (like Alt+Click) temporarily disables the breakpoint.
Remove permanently removes the breakpoint.
Properties lets you configure the breakpoint in the Breakpoints panel.
you run the debugger, it prepares and evaluates the configured
breakpoints. When this happens, the breakpoint becomes either verified or invalid, and the icon in the gutter area
updates to reflect this state. If the breakpoint is refused and
becomes invalid, in most cases it means there was an absence of
debugging information or the information doesn’t correspond to
the source code. In some cases, recompiling your sources may solve
the sync problem.
and disabling breakpoints
can permanently remove any breakpoint from your project by selecting
its entry in the Breakpoints panel and clicking its tab’s Remove button. To temporarily disable a breakpoint without
removing it, deselect the checkbox next to its definition. You can
also remove and disable individual breakpoints through their icon by
clicking and Alt+Clicking, as described earlier
can now use drag and drop to reposition breakpoints in your code.
Grab the breakpoint with your mouse, and drag it to its new home.
with line number breakpoints
you’ve worked with other debuggers, you’re already
familiar with line number breakpoints. These are the most
common type of breakpoints and are a staple of the debugging process.
Quite simply, they’re used to target a particular section of
code for debugging.
at a line number
number breakpoints are triggered when the program reaches the
specified line of source, before the line is executed. It’s
possible to define a line breakpoint that is never reached during
program execution, in which case it’s ignored.
or unsetting a line number breakpoint is easy. Bring up the source
file in question in the editor, position the cursor anywhere on the
line you wish to target, and do one of the following to toggle the
breakpoint on or off:
Choose Run | Toggle Line Breakpoint (Ctrl+F8).
in the gutter area next to the line.
set, a line number breakpoint remains in the project until it’s
removed. Note, however, that the breakpoint is assigned to the line
number, not to the code. Moving or altering the line of code moves
its corresponding breakpoint, but the breakpoint obtains an invalid
status until you reload the class.
breakpoints can only be set on lines of code that can be executed by
Comments, declarations of fields or methods, and empty lines aren’t
valid locations for line breakpoints. If one of your breakpoints is
invalid, it appears with the invalid breakpoint icon during your
debugging session and is ignored.
the debugger shows some of your breakpoints as invalid even though
they’re assigned to lines of code you know to be executable,
your source code may be out of sync with the running application. Try
recompiling your application and rerunning the debugger. You can even
avoid recompiling and restarting your application by using IDEA’s
HotSwap feature, which lets you reload classes within a running debug
session (via the Run | Reload Changed Classes command).
with method breakpoints
breakpoints allow you to target your debugging sessions by the
method, rather than by line number, that you wish to investigate.
They let you follow program flow at the method level as well as check
entry and exit conditions. Unfortunately, relying on method
breakpoints can slow down the application you’re debugging, so
use them sparingly.
a method breakpoint
create a method breakpoint by placing your cursor inside the method
in question and selecting Run | Toggle Method Breakpoint from
the menu bar. The method breakpoint icon appears in the gutter area
next to the method declaration.
doesn’t matter where inside the method your cursor is when you
place the breakpoint. The breakpoint applies to the method as a
whole, and the icon always appears next to the declaration
statement. Left-clicking the icon removes it, whereas right-clicking
brings up a context menu allowing you to modify the breakpoint’s
can also Alt+Click the gutter area next to the line where the
method is declared, to set a method breakpoint basing on the context
of the line.
on method entry or exit
the Watch group of the Method Breakpoints options panel
are two checkbox options: Method entry and Method exit.
These selections control the points in the method access at which the
breakpoint’s action is triggered. You must enable at least one
of these method trigger conditions, both of which are on by default.
Select or deselect the boxes as required.
with exception breakpoints
breakpoints allow you to tell the debugger to respond to thrown
exceptions. Unlike the line number and method breakpoints, which
require specific source references, exception breakpoints apply
globally to the exception condition, not to a particular code
reference. Exception breakpoints are a great shortcut to finding the
source of problems stemming from a Throwable condition deep in the bowels of your code, without the fuss of having
to track down the exact source of the problem yourself. (What can we
say? We’re as lazy as the next guys.)
when a certain exception is thrown
of their global nature, you must use the Breakpoints dialog to
create exception breakpoints. Select the Exception Breakpoints tab, and click the Add button to create a new breakpoint.
You’re presented with a dialog for selecting the exception you
wish to break on. You can select any Throwable class from your Classpath. Once a breakpoint is set for a particular
type of exception, its action is triggered when the exception is
thrown anywhere in the code. If this is too aggressive for you, you
can narrow the scope through the use of class filters.
very handy feature is the Any Exception option of the Breakpoints dialog. It’s a global option that can’t
be removed, only disabled. Activating this option breaks the
execution of the application at any point where it causes an
exception, even if no breakpoint is explicitly set for that
exception. Using Any Exception quickly illustrates any
exception paths in the application’s execution.
between caught and uncaught exceptions
The Notifications option group for exception breakpoints provides
options for responding to caught and uncaught exceptions. By
selecting some combination of these two options, you can control the
type of exceptional condition that should trigger this breakpoint. If
a box is deselected, exceptions in that situation won’t trigger
the debugger action.
with field watchpoints
watchpoints allow you to target your debugging search to specific
instance variables. For example, if at the end of a complicated
process you’re ending up with an obviously wrong value on one
of your fields, then setting up a field watchpoint may be the
quickest way to determine the origin of the fault.
a field watchpoint through the Breakpoints configuration window
you set up a field watchpoint from the editor when the caret is on
the line of the field declaration. Select Run | Toggle Field
Watchpoint from the main menu, or Alt+Click the gutter
area next to that line. You can also set a field watchpoint through
the Breakpoints dialog. Select the Run | View Breakpoints menu item to bring up the Breakpoints dialog (or press Ctrl+Shift+F8), and select the Field Watchpoints tab.
other types of breakpoints, any currently defined field watchpoints
are listed here. Click the Add button to bring up the Add
Field Watchpoint dialog, and specify the class and field name you
want to break on. The class name you provide here must be fully
qualified. Using the selection buttons next to each field makes this
process easier, because you can navigate to the desired class and
field by name or through a tree view of your project.
added, your field watchpoint displays a corresponding icon in the
editor’s gutter area. It behaves like other breakpoint icons:
It can be disabled, enabled, or removed directly from the gutter
can set field watchpoints directly through the editor. To do so,
right-click the line with declaration of the field you want to watch,
and then select the Toggle Field Watchpoint option from the
context menu. This sets the breakpoint and opens the Breakpoints dialog.
a field watchpoint while debugging
is another way to add a field watchpoint. Although it’s more
convenient, it can only be used once a debugging session is underway.
After you’ve suspended your application, you can add a field
watchpoint for an instance variable by right-clicking its icon in the
frame view and selecting the Add Field Watchpoint menu item.
This command creates the appropriate field watchpoint and opens the Breakpoints dialog. In addition, it prepopulates the instance
filter (described in the next section) with the instance ID of the field’s source object.
on field access or modification
of how you set up your field watchpoints, you must specify under
which circumstance you want the debugger to trigger the breakpoint.
These options are located in the Watch group of the Breakpoints dialog panel.
first option, Field access, tells the debugger that you wish
to trigger the breakpoint any time the field is accessed. This could
involve initialization, read access, or write access. The second
option, Field modification, says that you only care about
changes to the field. Simple read attempts won’t cause the
breakpoint to trigger. You must select at least one of these
needn’t be absolute. You can also create breakpoints that take
effect only under certain conditions and situations. This gives you a
much finer degree of control, allowing you to spend more time
debugging and less time stepping through program code.
only when a certain condition is true
option lets you specify a logical condition for activating the
breakpoint’s action. The condition is any valid Java expression
that evaluates to a boolean (true/false)
value at runtime. This expression is evaluated in the context of the
current line, so it can use any available fields, methods, or
variables available at that point. For example, any of the following
represent valid boolean expressions, assuming the referenced variables are accessible at the
!= 0 && response > 12
that these are expressions, not lines of code or statements—don’t
end the expression with a semicolon. To set the break condition,
enter the expression in the Condition field of the Conditions option group, and enable it by selecting the appropriate checkbox.
Your expression isn’t validated until the debugger reaches it,
so type carefully! If you make a mistake and enter an invalid
expression, you’ll be notified when it’s reached and
given the option of breaking.
can use IDEA’s
code-completion features to help construct your expression.
only for certain instances
The Instance filters option lets you activate a breakpoint for
particular instances of the class within the VM.
Each object in the VM is assigned an instance ID at creation time. You can see this instance ID in the debugger’s view of the stack frame as the number
following the @ sign next to an object reference. Unfortunately, you can’t
predict the instance ID value—it’s not necessarily sequential. Instance IDs
change from run to run, so this conditional breakpoint setting must
be reset with each run.
enabled, the breakpoint applies only to an object’s instance
whose ID is included in the instance filter list. This option is useful for
following the progress of a single object instance as it travels
through code paths shared with many other instances, such as when
you’re working with objects in collections.
only for certain classes
can use class filters to narrow the scope of breakpoints so they
apply only to particular classes. Simple wildcard expansion can be
used to match the fully qualified class name. The filter format is
limited to either an exact match or a pattern match beginning or
ending with an asterisk. For example:
com.acme.conversion.currency.Currency matches a single class, Currency.
com.acme.conversion.currency.* matches all classes in the currency package and its subpackages.
java.* matches all classes in the core java packages.
*Listener matches any class whose name ends with Listener.
create a set of class filters for a breakpoint, enable the Class
filters option and type in the pattern or list of patterns
delimited by spaces. By using a minus sign before a pattern, you can
exclude matching class(es) from the scope.
an alternative to typing, you can click the ellipsis button next to
the text box to show the Class Filters dialog; there you can
specify the set of patterns using the IDEA’s find class by name feature.
after a certain number of passes
option lets you create breakpoints that become active only after they
have been reached a certain number of times. To enable this
condition, enter a numeric value in the Pass count field of
the Conditions option group, and select the checkbox. The Pass
count value is the threshold for enabling this breakpoint,
meaning that the breakpoint is active for all subsequent passes once
this threshold has been crossed. It doesn’t specify which
execution pass to debug, but rather the number of passes to skip
option is useful for debugging loops or other lines of code that are
called repeatedly. For example, if you want to examine the last
iteration of a loop that runs 10,000 times, but you don’t feel
like stepping through it for an hour, set the Pass count option to 9,999 to trigger your debugging action on the last
The Pass count condition is mutually exclusive with any other
If you liked this excerpt from the book “IntelliJ IDEA In Action“, do check out the book.