Learn Programming in Java

<<Previous | ToC | Next >>

Using the Java (BlueJ) Debugger

When your program is broken, when it doesn't do what you thought you told it to do, we say "It has a bug." The term "bug" came out of the earliest computers, which used mechanical relays to do their logic. One day they were getting wrong answers and the technicians finally isolated the problem to a moth that was trapped between the contacts of one of the relays, preventing it from completing the circuit. Now everything is software, so instead of technicians making circuit measurements with a volt meter, we have a software program whose job it is to show what is happening inside your program, and it is called a "Debugger" because its purpose is to help you find (so you can remove) the bugs in your program. The debugger does not find anything, you do that. The debugger merely shows you in a graphical way what you told the computer to do, so it's easier for you to see how that is different from what you wanted to tell it to do. Only you know what you wanted; the computer only knows what you actually told it, and it does exactly what you told it to do.

So here's the deal: I ran my Java Guessing Game, and well, it didn't work properly, like it wasn't seeing my input, other than to repeat the input loop (see screen shot).

If you are doing this on Replit, see special instructions here. Otherwise...

In the BlueJ dashboard window (that's the one with the yellow squares) double-click the GG box (or if you did your in the Hello class, that one), we want to open the source text. I did it while the program was still running, and it worked in BlueJ, but other IDEs may not. Click in the left margin of the source text, and it will plant a little red stop sign (or dot or some mark there) which we call a breakpoint:

Then I typed my next input in the terminal/console window. When the execution gets to the breakpoint, it will stop in the debugger, like this:

Debuggers generally give you these three things:

1. What subroutines called whom to get here (in this case we are still in the main)

2. The values of the local variables (BlueJ gives also static variables and instance variables, others may not)

3. Your choice to

a. Kill the program because you now know what the problem is, or you know what to reprogram to help find the problem you don't yet know; or

b. Execute one line of source code, so to see what that line does, or

c. Run full speed until another (possibly the same) breakpoint is hit.

Many debuggers also give you the option to step into a subroutine (so it stops at the first line inside), or else to step over it, as if the subroutine call were like any other line. If not, you can always set a breakpoint there to get the same effect.

For now we are interested only in the "Local variables" panel, which we can scroll to see everything, or sometimes enlarge by dragging the dividing lines between the other panels away (up or to the left). Since I put the breakpoint right after the input call, I get to see the value in answer, which is 'y' (what I typed), but it's not equal to the 'Y' or 'N' that I am comparing it to on the next line. In Java text and character comparisons are case-sensitive (they are in Kitchen too, but it tries to be accommodating).

That's the problem:the Kitchen computer capitalizes single character input for me, but Zystem apparently does not. I wrote the bugger a year ago, I guess I forgot. It happens. (Reminder to myself: Kitch 1-character input capitalizes; add another line in Java to do the same). If you look up an ASCII character chart on the internet (or here, in "Need to Know"), you can see that the lower-case Roman letters are +32 higher in (integer) value than their respective capitals. Java probably has conversion functions that also work for foreign languages, but we're only looking for "Y" or "N". Another way would be to test for both capital 'Y' and lower-case 'y' (and the same for 'N').

A stronly typed language like Java (should be) will not let you do arithmetic on characters, so you use type-casting (the name of the type in perentheses in front of the value) to convert the character to an integer, then do the math, then cast it back to a character:

if (answer >= 'a') if (answer <= 'z') answer = (char)(((int)answer)-32);
I inserted this right after the input, and before the program tested answer. With this extra line, the program now works.

If it had not, then I would have stepped farther into the program to see if maybe the new top or bottom is not being set correctly. You never know what the problem is (if you did, you could fix it without the debugger ;-)

I first wrote this page five years ago, but programmers are an anarchic lot -- not you, I hope -- and they are constantly "improving" things, "fixing" things that are not broken, with the result that stuff that used to work breaks. Today, if I set the breakpoint on the input line, then click the Step button and enter the input, the program didn't stop at the next line, it keeps running until it comes back around. We call a mistake like this a "bug" but it's their bug, not yours. By the time you do this, maybe they will have fixed the bug and it will work correctly again. Or not. If it doesn't stop at the next line, click the red "X Terminate" button and put another breakpoint on each line after a "Zystem.ReadLetter()" then start over. You might want to clear the Terminal Window (from the menu) before you restart. When you get to each "Zystem.ReadLetter()" line, click the "Continue" button instead of "Step" and your program will run at full speed until it gets to the next breakpoint. A lot of debuggers have this particular problem at one time or another, so I do this a lot. You will get used to it.

If you have a small screen like the laptop I'm testing this on today, you have four windows each filling more than their share of the screen. I try to arrange the windows (slide them around) so that a piece of each window is showing off in one corner or another, so that no matter which window is in front, some corner of every window is still visible, and I can click on the visible corner of any other window and bring it to the front when I need to look at it. Many programs have a "Windows" menu, and you can choose the window you want from the menu. The version of BlueJ I'm using today has such a menu, but they didn't do it correctly, it's only visible when you bring the console window to the front (which if it's buried is not much help). This is not possible in environments like Replit that give you panes instead of windows, I guess they assume everybody has a huge screen.

Here, for example, I placed the debugger window in the lower right corner, the source code window in the upper right corner, the terminal (input & output) window in the lower left corner, and the BlueJ "StartHere" console window in the upper left. Some of the corners are offset slightly to make it easier to see which windows go with which corners. Once you like where the windows are, if you don't keep moving them around they might stay there, even after you quit and resume the next day. Or not: unix programmers think they know more than you do about where you want your windows.

An important part of computer programming is being able to think like a computer, and watching the debugger step through and do its thing is a good way to learn that. Eventually you should be able to think through the steps without watching them most of the time. All of us need to look occasionally, because we make mistakes, and it's sometimes hard to see through what we are thinking (when it is wrong) to what the computer is actually doing.

Next: Rock-Paper-Scissors

<<Previous | ToC | Next >>

Revised: 2021 October 5