Copyright © Cay S. Horstmann, Kathleen O’Brien 2009-2014
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
When you hear the person sitting next to you saying “I have a bug in my program”, you don't have to worry about roaches infesting the computer. When we say bugs we really mean logical errors in our program. The first bug, however, was a real live bug. Well, live at first. In 1947 at Harvard University, a moth was found in one of the components of the Mark II computer, and was causing problems.
In this lab you will deal with debugging. You will practice locating and fixing some common types of bugs. The high point of the lab is a tour of the debugger inside the BlueJ environment.
Make a BlueJ project from the files WordAnalyzer.java and WordAnalyzerTester.java.
Have a look at the WordAnalyzer class. A WordAnalyzer is constructed with a string—the word to be analyzed. Right now, we only care about the first (buggy) method: firstRepeatedCharacter. It returns the first repeated character in a word, such as o in roommate.
The WordAnalyzerTester program simply tests the WordAnalyzer class.
Step 1 |
Without actually running the program, predict its output. Assume that the firstRepeatedCharacter method works correctly. Scribe: Record your prediction. |
Step 2 |
Now run the program. Driver: What output do you get? (Include the correct outputs and the error message.) |
The program dies with an exception and prints a stack trace:
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 4
at java.lang.String.charAt(String.java:558)
at WordAnalyzer.firstRepeatedCharacter(WordAnalyzer.java:26)
at WordAnalyzerTester.test(WordAnalyzerTester.java:14)
at WordAnalyzerTester.main(WordAnalyzerTester.java:7)
Have a look at the stack trace. The first complaint is about the method charAt of the java.lang.String class. That's a library class. It is extremely unlikely that a library class has a bug. It is far more likely that your program called one of its methods with bad parameters.
The next complaint is about line 26 of the firstRepeatedCharacter method of the WordAnalyzer class. To find line 26 easily, turn on line numbers.
Note: To display line numbers in BlueJ, click Optionsin the main menu. Select Preferences and then check the box for Display line numbers
Step 3 |
Driver: Exactly what source code do you find in line 26 of WordAnalyzer? Past it into your report. |
Now look at the call to charAt in line 26.
word.charAt(i + 1)
Step 4 |
In theory, there are two different exceptions that can be thrown in this call. Scribe: What are they? |
Of course, we know that word is not null in this case (So it can not be a NullPointerException), so we know that the error must be an index out of bounds exception.
Step 5 |
(a) Scribe: Explain the
nature of the bug, and how to fix it. (b) Driver: What output did you get after you fixed the bug? |
Exceptions are useful to pinpoint drastic errors that kill your program. But
often, a sick program doesn't die. It limps along and computes the wrong
result. In order to find out what such a program does, students often sprinkle
their code with print statements like this one:
System.out.println("Yoohoo! I got this far!"); System.out.println("i=" + i);
Print statements can be effective, but they can take a lot of your time. You put them in, you take them out, you put them back in, you comment them out, you remove the comments, and when it all works you take them out for good. Until the next bug appears. |
But there is a better way, especially as programs get bigger and more complex. Most development environments, including BlueJ and Eclipse, have a debugger, a tool that lets you execute a program in slow motion. You can observe which statements are executed and you can peek inside variables.
Debuggers can be complex, but fortunately there are only three techniques that you need to master:
For this section, you need the WordAnalyzerTester3 class that tests the buggy countRepeatedCharacters method. That method counts the substrings consisting of repeated character in a word. For example, the word mississippiii has a count of 4. (Yes, it is misspelled : ) )
Step 1 |
Execute WordAnalyzerTester3 in BlueJ. Driver: What output do you get? |
The countRepeatedCharacters method does the right thing for the first two test cases, but it mysteriously fails on the string "aabbcdaaaabb". It should report 4 repetitions, but it only reports 3.
Unfortunately, the debugger cannot "go back", so you can't simply go to the point of failure and backtrack. Instead, you first run your program at full speed until it comes close the point of failure. Then you slow down and execute small steps, watching what happens.
We know that the first two calls to the test method in
WordAnalyzerTester3 give the correct results. It won't be too
interesting to debug them. Instead, let's go directly to the third call. We'll
set a breakpoint at that line, so that the debugger stops as soon as it
reaches the line.
In BlueJ, open the WordAnalyzerTester3 class for editing. Click left of the
vertical line in the line containing the third call to test
. A red
stop sign should appear, indicating the breakpoint.
Step 2 |
Launch the debugger as described. Driver: What output do you get in the terminal window? Scribe: Why do you get two lines of output and not three? |
When the debugger stops at a breakpoint, another window appears, with a row of big buttons on the bottom:
You use the Step and Step Into buttons to step through your code in slow motion.Step 3 |
Execute the "Step Into" command. Scribe: What happens? |
WordAnalyzer wa = new WordAnalyzer(s);That call is boring, and we do not want to step into the constructor. We'll use the "Step" command instead.
Step 4 |
Execute the "Step" command. Scribe: What happens? |
int result = wa.countRepeatedCharacters();
Step 5 |
Remember, we want to find out why we get the wrong repetition count for the third test case. Scribe: Should you execute "Step" or "Step Into" at this point? Why? |
Execute "Step Into" a couple of times. You should get into the lines
int c = 0;
and
for (int i = 1; i < word.length() - 1; i++)
Look inside the Local variables window in
the bottom right corner.
Step 6 |
The value of c increases three times. Scribe: What are the values for i at each increase? |
Step 7 |
Look at the value of the word instance variable. Scribe: What is special about the three positions at which c increases? |
Step 8 |
Scribe: What happens when you click the "Continue" button? |
if (word.charAt(i) == word.charAt(i + 1))tests for yy, that is, a character that is followed by another one just like it. But if we have a sequence yyyy, we only want it to count once. That's why we want to make sure that the preceding character is different:
if (word.charAt(i - 1) != word.charAt(i))This logic works almost perfectly: it finds three group starts: aabbcdaaaabb
Step 9 |
Scribe: Why doesn't the method find the start of the first (aa) group? |
Step
10 |
Scribe: Why can't you simply fix the problem by letting i start at 0 in the for loop? |
Step
11 |
Go ahead and fix the bug. (a) What is the code of your countRepeatedCharacters method now? (b) Run the WordAnalyzerTester3 method again. When you get to the breakpoint, click continue to run full speed to the end. What is the output now? |
Is the program now free from bugs? That is not a question the debugger can answer. As the famous computer scientist Edsger Dijkstra pointed out: "Program testing can be used to show the presence of bugs, but never to show their absence!" As you have seen in this lab, testing and debugging is a laborious activity. In your computer science education, it pays to pay special attention to the tools and techniques that ensure correctness without testing. |
Driver: Run the tester again. When you get to the breakpoint, take a screen shot of the Bluej debugger plus the editor window showing the breakpoint. Upload it into Canvas along with your report. If you do not know how to do this, you need to ask. This is an important skill (and you will need to do it on the upcoming exam.)
Either use this Bluej project or try the process on a homework with a failing test case.
The given Bluej project has two bugs. Can you find them? It is suppose to return a string containing the vowels in the word. The first bug is an exception. After you fix that problem, run it again. Look at the output. Ask yourself "Why are some results blank? Why are some printing out one vowel repeatedly?"
1. Describe what you do to get close to the point where the bug happens.
2. Describe the values of all variables just before the bug happens.
3. Single-step over the place where the bug happens. Did it happen?
4. When you look at the values of the variables, can you tell why the bug happened? Which variable wasn't set to the right value?
5. Did you get enough information to fix the bug?
Complete Quiz20 if you want to leave the lab early.
Ask the lab instructor if you have questions.