Debugging Java code – A beginner’s guide

A lot of beginners struggle with finding errors in their programs. This is a short tutorial to help you get started with some basic debugging in Java. The tutorial is aimed towards small programs, for example something you’d find yourself writing as homework or in a coding exercise. However, that doesn’t mean that the usefulness of the tips end there.

If you’re reading this because you’re new to programming and got some mysterious errors while compiling one of your first programs I’d suggest that you be patient. The tutorial is in fact very short, and you’ll be a lot more independent once you grasp the basics. Let’s get started!

Stack traces

The first thing you need to learn is how to read a stack trace, so let’s get our hands on one:

public class Homework {
    public static void main(String[] args) {
        foo();
    }

    static void foo() {
        int a[] = new int[5];
        a[5] = 3;
    }
}

Compiling and running the above code generates the following stack trace:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
	at Homework.foo(Homework.java:8)
	at Homework.main(Homework.java:3)

There are three interesting things to note here:

  1. The name of the error:   ArrayIndexOutOfBoundsException
    which tells us that we did something wrong with the bounds of an array. Searching for the meaning of parts of an exception, such as “Out of bounds” is a good idea if you don’t understand what part of it is referring to. There’s also documentation that explains the error. The java documentation will always be the top hit if you search for class or exception names. You can’t miss it.
  2. The actual “trace”. The last two lines show us where the error occurred by listing the method calls leading up to the line which caused the exception (starting at the error and working its way towards the original method call). These lines even show us which files made these calls, and at which lines the calls occurred.
    at Homework.foo(Homework.java:8) means that the exception occurred in a method called “foo”, in a file called Homework.java, at line 8. The last line tells us where the method “foo” was called.
  3. The value that caused the problem, 5.

With this in mind we can quickly deduce that we went beyond the size of the array at line 8 since arrays in Java are 0-indexed. If you didn’t know that arrays are 0-indexed it becomes a little trickier. In that case I’d suggest that you go beyond the documentation when searching for the specific exception. For e.g. ArrayIndexOutOfBoundsException my second hit is a Stackoverflow thread which explains this in a very clear and concise way.

Most of the time, knowing what and where is all you need. The how is often obvious once you revisit your code, as in the case above.

Debugging by printing

If your code has a logic error you don’t have any stack trace to help you figure out what’s wrong. Instead, you need to study what the program is doing, and where it has unintended behaviour.

Below is a method that is supposed to tell us if a String is a palindrome or not, but it is currently broken. In order to diagnose the program I’ve added three debug prints (denoted by //debug comments) which should help us find the problem. I have a sentence which is a palindrome, but the method is returning false. I want to see why it is returning false, so I’m printing the values that made it return false.

public class Homework {
    public static void main(String[] args) {
        System.out.println(is_palindrome("No x in Nixon")); // false?
    }

    static boolean is_palindrome(String word) {
        char[] chars = word.toCharArray();
        for (int i = 0; i < chars.length/2; ++i) {
            if(chars[i] != chars[chars.length-1-i]) {
                System.out.print("Comparing '"); // debug
                System.out.print(chars[i] + "' and '"); // debug
                System.out.println(chars[chars.length-1-i] + "'"); // debug
                return false;
            }
        }
        return true;
    }
}

The program has the following output:

Comparing 'N' and 'n'
false

As you can see, it does not think that ‘N’ and ‘n’ is the same character (because they aren’t). Looking at the String documentation I find the useful method toLowerCase(). Adding

word = word.toLowerCase();

to the top of the method should fix the problem.
Note: toLowerCase() returns a new String, which we have to assign to a variable.
It works like this because Strings are immutable.

Let’s see what the edited program outputs:

Comparing ' ' and 'x'
false

There’s another problem. The sentence is only a palindrome if we ignore spaces, and since spaces are part of the String (and therefore the char array) they are messing with our algorithm. A little searching guides us to the following popular solution, which removes all spaces from a String:

word = word.replace(" ","");

The above code means that we want to create a new String object where all spaces are replaced by nothing, i.e. removed. We assign the new String to the old variable.
Note: String.replaceAll with a regular expression works just as well (see the top answer in the link),
but I wanted to keep the solution simple. The difference is that it replaces all whitespace characters, such as newlines, tabs and spaces.

The new output is:

true

The program works!

The lesson here is that printing is a powerful tool that can help you understand the flow and inner workings of your program. You can print messages to see which methods are called and in which order, and with which parameters. You can study the value of your variables at different states, and much more. Though, remember to be patient. Understanding your own output might not be trivial. Just remember that programs only do what you tell them to do.

You’ll soon be able to fix, or at least pinpoint, even the most insidious bugs in mere minutes.

Debuggers

Last but not least there are programs known as “debuggers”. These programs are made specifically to find bugs in your code and most IDE’s have one installed by default. How to use them is beyond the scope of this post since each debugger is different. If you want to get into more advanced debugging with some very efficient tools such as breakpoints, step-by-step execution, and variable tracking, I’d suggest that you find a tutorial that covers your specific programming language and environment.

Good luck fixing those bugs, and feel free to leave a comment!