In this topic, we are going to learn how to use a debugger to test and debug simple constructs like if statements and loops. Of course, the functionality that we'll cover is not limited to just loops: you will be able to apply these tools to solve a wide range of problems.
If statements
First of all, we might want to test if the program visits a branch of an if statement at all.
It happens from time to time that we assume the boolean expression in an if statement to evaluate to true, and for some reason get unexpected results. In such a situation, it is sometimes useful to test if the code inside if is really executed.
Let's take a look at the following simple program:
void whatever() {
String greeting = "Hello world";
if (greeting.contains("e")) {
greeting += "!";
System.out.println(greeting);
}
System.out.println("Shutting down");
}
In order to find out if the code inside if gets executed, do the following:
Click the gutter against the first line in the
ifblock. A red circle appears: this is a line breakpoint. It will tell the debugger to suspend the program execution whenever this line is executed.Click the Debug button that is located next to Run and Run/Debug Configurations. Apart from building, installing, and running the app (like Run), Debug will also attach a debugger.
The line with the breakpoint turns blue. It means that the program was just about to execute it, but it was suspended and now it waits for commands from you.
The suspended state allows you to examine the variables, control the execution step-by-step, and even alter its outcome. This is, however, a subject for further topics. For now, we've just made sure that the program has executed theifin question. If it hadn't, then we wouldn't have stopped at this line, because the program would've never reached the line with the breakpoint.
Let's resume the program so that it can execute the remaining statements and terminate. You can do this by pressing Resume in the Debug tool window.
You can use this technique to debug any code, not only if statements. For example, if you want to test if a certain method gets executed, just put a breakpoint to see if you stop there. If you don't, then you know that the program hasn't executed this line and hasn't called the method.
This might seem very simple, and you may ask why we need a debugger for that. The answer is that in real life, you sometimes have to deal with very convoluted code that leads to subtle bugs. To track such bugs, we often need to understand if a piece of code runs at all, and this is where basic debugger features like this one can save you a lot of time.
Loops
Let's learn how we can debug loops. In this example, we are going one step further and will add some breakpoint conditions and examine the state of the program. Sounds thrilling, doesn't it?
Just like with if statements, you can set a breakpoint inside a loop, and the program will suspend it each time it executes the line, that is, with each iteration of the loop. While this may still be useful for short loops, the problem arises when a loop is going to iterate, say, 1000 times, and we are only interested in some specific iteration. It would take a very determined person to continuously resume a program 1000 times. We have a better way, though.
Here's our example:
void whatever() {
char rangeStart = 'C';
char rangeEnd = 'Y';
char findLetter = 'Q';
for (char c = rangeStart; c <= rangeEnd; c++) {
if (c == findLetter) {
System.out.printf("Character %s is within range %s-%s",
findLetter, rangeStart, rangeEnd);
return;
}
}
System.out.printf("Character %s is not within range %s-%s",
findLetter, rangeStart, rangeEnd);
}
Let's say we are interested in the iteration that tests the character H:
As in the previous example, set a breakpoint somewhere in the loop.
Right-click the breakpoint, and specify the condition:
c == 'H'. The condition is just a boolean expression that must evaluate totruein order for the program to stop at this breakpoint. Note that the program evaluates the condition in the context of the code that the breakpoint is set in. For example, the conditionc == 'H'will not work for a breakpoint outside the loop, because the variablecis not visible there.Start debugging by either clicking Debug or attaching a debugger to the existing process.
The program stops in the loop. In contrast with the previous example, the debugger has not suspended the program every time it hit the breakpoint. We didn't stop until the loop was checking the letter
H.Let's examine the variables in the Debug tool window. Its purpose is to show you information related to the current state of the program and suggest a way to interact with it. For now, let's look at the Variables tab. It provides information on all variables accessible in the current context. Here, you can see the values of
rangeStart,rangeEndandfindLetter, as well as the loop's local variablec.Resume the application, letting it run further.
Conclusion
We have started to scratch the surface by debugging some basic examples, yet it's enough to solve some of the real-world problems.
Speaking of problems, the knowledge you've just gained can help you solve some of the tasks from other topics many times faster than it would take you if you didn't use a debugger. For example, when they ask you what the contents of the array will be after the n-th iteration of a sorting loop: now, with the help of a debugger, you have a simpler way of solving this.
As we have already mentioned, the debugger can be used in various ways, not necessarily related to finding bugs. After you gain some experience, it will surely become one of your most indispensable tools.
See you on further topics!