You are on page 1of 9

Debugging Code

Preface
As you start working with Object Oriented Programming, you’ll encounter multiple files, classes, inheritance, and
other components that make debugging more challenging. No longer will you be working with all your code in a
single file. As such, we’ve put together this debugging guide that illustrates some common errors and how to debug.
List of Common Errors
There are quite a few errors in Java that you will regularly encounter.

Compile-Time Errors
These errors will occur before your program actually runs. These errors are thrown by the Java compiler:

Error Message What It Usually Means

something expected The parser was surprised by a symbol you wrote at or just before this point.

cannot find symbol class Make sure that file is saved in the same folder as the file that refers to it.

cannot find symbol method You got the method name wrong, or you called it on the wrong file/class.

class, interface, or enum expected You have too many closing braces.

class should be declared in a file named


Your class name and file name must match exactly.
something

illegal start of expression You're missing a closing brace for the previous method declaration.

incompatible types - expected type Make sure you understand why it found what it did, and why it expected what it did.

missing method body Your method declaration line has a semicolon.

The compiler found a pathway through your non-void method that does not reach
missing return statement
a return statement.

non-static method cannot be referenced


You called a method on a class name, instead of on a specific instance of that class.
from a static context

possible loss of precision You assigned a double value to an int variable.

reached end of file while parsing You're missing a closing brace at the end of the file.

unexpected type - required variable You used = instead of ==.

The compiler found a pathway through your method where you access the value of a
variable might not have been initialized
variable before you've assigned anything to it.

Run-Time Errors
These errors occur during the lifetime of your application or program. They may occur when clicking a button,
providing a certain input, or during regular execution. We’ve included some sample code to demonstrate a few of
these common runtime errors later in this document.

Error Message What It Usually Means

My program freezes You have a loop that never reaches its stopping condition.

ArrayIndexOutOfBoundsException You tried to access an array element with an index that was too high or too low.

NullPointerException You tried to call a method on null (or access an array element of null).

OutOfMemoryError You are constructing new objects inside an infinite loop.

StringIndexOutOfBoundsException You tried to access a character in a String with an index that was too high or too low.
IndexOutOfBoundsException
The IndexOutOfBoundsException occurs when trying to access an index larger than the last index for an object. This
occurs most frequently with Arrays (primitive), ArrayLists, and Strings, but it can occur almost anywhere. The
easiest way to debug this is using print statements. The following code throws an IndexOutOfBoundsException:

int[] arr = int[5]; // creates an empty int array with indicies 0, 1, 2, 3, 4


for(int i = 0; i <= 5; i++) {
arr[i] = i;
}
System.out.println(arr);

How can we debug this error? The easiest way is using System.out.println() statements. For this method, let’s
print out each number (i) that we are checking. Adapting the method:

int[] arr = int[5]; // creates an empty int array with indicies 0, 1, 2, 3, 4


for(int i = 0; i <= 5; i++) {
System.out.println(i); // added this for debugging
arr[i] = i;
}
System.out.println(arr);

we see the following output:

0
1
2
3
4
5
(error) IndexOutOfBoundsException

This means we are checking indices 0-5. However, we only have indices 0-4, since our int[] is of size 5. In this case,
our for loop is iterating one too many times. We need to change the <= to < and our code works! Here’s the final
solution:

int[] arr = int[5]; // creates an empty int array with indicies 0, 1, 2, 3, 4


for(int i = 0; i < 5; i++) {
arr[i] = i;
}
System.out.println(arr);

Note that you should always remove debugging statements from your final submission. This is discussed in more
detail in the Style Guide.
NullPointerException
The NullPointerException occurs when you try to call a method on a null object. A null object is an object that
doesn’t exist… it is “nothingness.” This occurs most frequently with LinkedLists and BinaryTrees, but you could
encounter it anywhere. The easiest way to debug this error is with System.out.println() statements. The following
code throws a NullPointerException:

String[] arr = {‘The’, ‘quick’, ‘brown’, ‘fox’, ‘jumped’, ‘over’, ‘the’, null};

// prints all the values in arr in lowercase


for(int i = 0; i < 5; i++) {
System.out.println(arr[i].toLowerCase());
}

This above code will produce the output:

the
quick
brown
fox
jumped
over
the
(error) NullPointerException

This is because we are trying to call the method toLowerCase() on a null object. null objects do not have methods,
so this breaks. In order to fix this code, we need to first check if the object is not null before calling toLowerCase():

String[] arr = {‘The’, ‘quick’, ‘brown’, ‘fox’, ‘jumped’, ‘over’, ‘the’, null};

// prints all the values in arr in lowercase


for(int i = 0; i < 5; i++) {
if(arr[i] != null) {
System.out.println(arr[i].toLowerCase());
}
}

This will output:

the
quick
brown
fox
jumped
over
the

Note that, when checking if objects are null, we use == and != instead of the equals() or !equals() methods. This
is because you cannot call methods on a null object.
Logic Errors
These are the most detrimental of all errors, because you don’t know they exist. They don’t (necessarily) cause the
program to crash, but rather produce invalid output. Writing unit tests for your classes most frequently catches
these errors.

Passing by Value vs. Passing by Reference


This can be a frustrating problem to diagnose, because when you look at the code, you might be sure that it’s
passing by reference, but find that its actually being passed by value. Java uses both, so you need to understand
when you're passing by value, and when you're passing by reference.

When you pass a primitive data type, such as a char, int, float, double, or boolean, to a function, you are passing
by value. That means that a copy of the data type is duplicated, and passed to the function. If the function chooses
to modify that value, it will be modifying the copy only. Once the function finishes, and control is returned to the
returning function, the "real" variable will be untouched, and no changes will have been saved. If you need to
modify a primitive data type, make it a return value for a function, or wrap it inside an object.

When you pass a Java object, such as an Array, a Vector, or a String, to a function then you are passing by
reference. Yes – a String is actually an object, not a primitive data type. So that means that if you pass an object to a
function, you are passing a reference to it, not a duplicate. Any changes you make to the object's member variables
will be permanent – which can be either good or bad, depending on whether this was what you intended.

Comparison Assignment
This is an easy error to make. If you're used other languages before, such as Pascal, you'll realize just how poor a
choice this was by the language's designers. In Pascal, for example, we use the := operator for assignment, and
leave = for comparison.

Fortunately, even if you don't spot this one by looking at code on the screen, your compiler will. Most commonly, it
will report an error message like this: "Can't convert something to boolean", where something is a Java type that
you're assigning instead of comparing.

Using the Wrong Comparison Operator


When we use the == operator, we are actually comparing two object references, to see if they point to the same
object. We cannot compare, for example, two strings for equality, using the == operator. We must instead use the
equals() method, which is a method inherited by all classes from java.lang.Object.

String front = "abc";


String back = "xyz";

// Bad way
if (front == back) { … }

// Good way
if(front.equals(back)) { … }

Example Logic Error


Like we said before, logic errors are the most difficult to track down. Consider the following code snippet, which
takes a String parameter and returns the number of times the letter s occurs:

public int countLetterS(String input) {


int count = 0;
for(int i = 0; i < input.length(); i++) {
if(input.substring(1,1) == “s”) {
count++;
}
}
return count;
}

If you execute this code, it will run fine. However, you’ll get a count of 0, no matter what word you put it! Examining
the code, you might begin to add System.out.println() statements at various steps. If we add the following
debugging code:

public int countLetterS(String input) {


int count = 0;
for(int i = 0; i < input.length(); i++) {
System.out.println(“g” == “s”); # added for debugging
if(input.substring(1,1) == “s”) {
count++;
}
}
return count;
}

You’ll will notice that the expression “s” == “s” is always false. You quickly recall that the Java == method objects,
not object contents. You must use the equals() method:

public int countLetterS(String input) {


int count = 0;
for(int i = 0; i < input.length(); i++) {
if(input.substring(1,1).equals(“s”)) {
count++;
}
}
return count;
}

You run your code against the word “go” and it returns 1. Yay! You turn in your homework and get no credit for this
method. Why? You still have additional logic errors. Running a test suite with more words will demonstrate why:

so 1
hit 0
soso 1
fist 0
sift 1

You can see that it’s only checking the first letter of the word. It seems to be ignoring every other word. Jumping
back into our code, you can see that we are always looking at the same index in our substring() method:

input.substring(1,1).equals(“s”)

We actually need this condition to be base on i, our index iterator:

input.substring(i,i+1).equals(“s”)

Now if we run our final code:

public int countLetterS(String input) {


int count = 0;
for(int i = 0; i < input.length(); i++) {
if(input.substring(i,i+1).equals(“s”)) {
count++;
}
}
return count;
}

Everything works as expected! Just kidding, we still have logic errors in our code. Expanding our unit tests to the
following set of words, we get the following output:

so 1
hit 0
soso 1
fist 0
sift 1
Seth 0
seaShells 1

Immediately you should notice that the method is ignoring uppercase letters in it’s counting. Digging through the
Java API, you realize there are three routes you can take to alleviate this problem:

1. Document your method, indicating that it only returns lowercase letter counts
2. Convert the incoming input to all lowercase or uppercase before comparing using the equals() method
3. Use the Java String‘s equalsIgnoreCase() method

From an experienced programmer, you want to choose option 3. Here’s why:

1. You are restricting your program significantly. Eventually you will probably have to write a complementary
method that checks for all capitalized versions of the letter anyway. This is just bad practice. Furthermore,
you run the risk of programmers not reading your documentation. It’s unfortunate, but this is often the case.
2. Convert the incoming input to all lowercase or uppercase before comparing using the equals() method.
This seems logical and will work for the purpose of the method. However, in terms of efficiency, it’s
compares poorly to option 3. Why? The method toLowerCase() iterates over every character in the String
and converts to it’s lowercase equivalent. Then our method is iterating over each character in the String
and comparing it to the lowercase letter “s”. You are doing twice the work!

The moral of this story: You should always write very exhaustive unit tests – sometimes before you even start
coding. There’s a PDF on Unit Testing for reference and a quick start guide on the course website.
Reading a Stack Trace
At some point, unless you are the best programmer in the world, you will see something like this:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5


at MyFile.main(MyFile.java:5)

This is called a stack trace. It means that the Java runtime encountered an error. Stack traces range in length from a
single line to hundreds of lines of code. They provide a full trace of where java thinks the problem happened. Each
line will have certain properties – the method name, the line the method occurs on, the file name, the line number:

Method name Exception


Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
at MyFile.main(MyFile.java:5)

The index that was out of


Filename bounds

Line where method was called


Google is your Friend
You will rarely hear this in a programming class, but Google is your Friend. Rarely are you experiencing an error that
no one else has experienced before. Copy-pasting a snippet of your error message into Google will often yield
countless articles and possible sources of error.

Your Friend is not Google


Conversely, we take cheating very seriously in this class. Communicating about assignments with peers (either
enrolled or not) is strictly prohibited. All work, unless otherwise noted, is independent. The following conversation is
perfectly acceptable:

Ben: Hey Joe, I’m getting an IndexOutOfBoundsException on countLetterS().


Joe: You probably have your loop index going to far.
Ben: Dang, you’re right. Thanks!

The following conversation is not acceptable:

Ben: Hey Joe, I’m getting an IndexOutOfBoundsException on countLetterS().


Joe: Here, let me take a look at (or email me) your code.
Ben: Sure

The same is true for students who are not currently enrolled in the course (such as students who have previously
taken or been a TA for this course). There are only 3 people who are authorized to look at your code:

1. You
*
2. Any TA or CA currently employed by the course

3. Any Professors of the course

Using other Help Sites


There are countless resources on the web, most notably StackOverflow.com and Google Groups, where
programmers can ask questions and the community provides answers. You are allowed to and expected to use
these resources when searching for errors in your programming, however, you are prohibited from asking for help
directly. In other words, you can read questions and answers, but you cannot post your own questions about the
course or homework.

*
The official list of TAs and CAs is available on the course website

The official list of Professors is available on the course website

You might also like