A Beginner's Guide to Java Debugging
This is intended as a reference guide as well as a tutorial on how to get started with debugging Java programs. For beginners, we assume you have some basic knowledge of Java syntax and the JVM and memory model, and we recommend you start from the beginning. Here is a handy list of links in the document for quick-reference:
Throwables and Reading Stack Traces
public int getMagicNumber() {
int average = computeAverage(num1, num2);
return average * 42;
}
public int computeAverage(int first, int second) {
int average = (first + second) / 2;
return average;
}
Say you call the getMagicNumber() method. It will then call the computeAverage() method itself. When the computeAverage() method completes though, it has to know where to jump back to - in our case, the first line of the getMagicNumber() method. Java does this by keeping track of every method on a call stack - that is, every method that gets called, is "stacked" on top of the method it is called from. In our example, the call stack when computeAverage() is running would look something like this:

java.sql.SQLException: Can not issue NULL query.The above exception probably has very little meaning to you (if you are curious, it is taken from a piece of code that inserts XML into a database), and that's okay. What we do want to show you though is how to extract the information you need to begin pinpointing the "exceptional condition."
at com.mysql.jdbc.Statement.checkNullOrEmptyQuery(Statement.java:1471)
at com.mysql.jdbc.Statement.executeUpdate(Statement.java:1231)
at com.mysql.jdbc.Statement.executeUpdate(Statement.java:1335)
at DatabaseConnection.execWithGetKeys(DatabaseConnection.java:90)
at XMLInserter.commitArticleData(XMLInserter.java:99)
at XMLInserter$XMLHandler.endElement(XMLInserter.java:168)
at com.icl.saxon.aelfred.SAXDriver.endElement(SAXDriver.java:792)
at com.icl.saxon.aelfred.XmlParser.parseETag(XmlParser.java:1133)
at com.icl.saxon.aelfred.XmlParser.parseContent(XmlParser.java:1217)
at com.icl.saxon.aelfred.XmlParser.parseElement(XmlParser.java:1037)
at com.icl.saxon.aelfred.XmlParser.parseContent(XmlParser.java:1222)
at com.icl.saxon.aelfred.XmlParser.parseElement(XmlParser.java:1037)
at com.icl.saxon.aelfred.XmlParser.parseDocument(XmlParser.java:510)
at com.icl.saxon.aelfred.XmlParser.doParse(XmlParser.java:163)
at com.icl.saxon.aelfred.SAXDriver.parse(SAXDriver.java:320)
at javax.xml.parsers.SAXParser.parse(Unknown Source)
at javax.xml.parsers.SAXParser.parse(Unknown Source)
at XMLInserter.main(XMLInserter.java:45)
Common Exceptions and Errors
There are three distinct classes of error messages Java gives you: checked exceptions, unchecked exceptions, and error messages (generally checked and unchecked fall under the "Exceptions" vs. "Errors" category and is further refined, but since they provide different kinds of information, and involve different debugging styles, we'll treat them separately).
String str = null;
System.out.println(str.length());
This code will throw a NPE (for short) on the second line. Here is a more complex example:
LinkedList<Request> queue = new LinkedList<Request>(); Request req = new Request(); req.prepare(); req.setMessageBuffer(null); //assume setMessage takes a StringBuffer object
String message = queue.poll().getMessageBuffer().toString();
This example is a bit more complicated. Let's see why.
NullPointerException:
at SomeClass.someMethod(SomeClass.java:16) at SomeClass.someParentMethod(SomeClass.java:45)
at SomeClass.main(SomeClass.java:90)
For this contrived example, assume line 16 corresponds to:
String message = queue.poll().getMessageBuffer().toString();
A NullPointerException usually will not give you any descriptive message, as most other Exceptions do. Luckily, the fact that it is an NPE, along with the location, is information enough for us.
NullPointerException:
at SomeClass.someMethod(SomeClass.java:16)
at SomeClass.someParentMethod(SomeClass.java:45)
at SomeClass.main(SomeClass.java:90)
A BRIEF INTERLUDE - DEBUGGING BY println()
When you are running a Java program, as far as we know, we cannot stop it. It either runs to completion or till an error/exception kills it. But what if we need to find out information while the program is running? What if we want to know the contents of a variable at any particular time?
The answer that may already have occurred to you is to use System.out.println(). You can print out the contents or toString() of any variable (this is why it's useful to override toString()). When the program reaches your statement, it will, quite simply, print out what you ask it to. It is generally useful to also print out some sort of label along with your variable so you know what's being printed and don't get confused. If you're printing out the index of a for loop, you may want something like:
System.out.println("i = "+i);If your program is dying somewhere though and you don't even know where to look or what variables to print out, you can still use the println() debugging method. Take one line, such as:
System.out.println("I'm still alive!");and place it smack before the return statement or end of the method/piece of code you suspect is causing the error. Run your program. If the words "I'm still alive!" are printed out, you know that your program executes till that point, and you may be looking at the wrong method (you may want to repeat the process one level higher in the call stack). If they aren't printed out, you know the program dies somewhere between the "start" of the program and the "end" of that method.
Now, we need to narrow our scope of where the error could be. Cut and paste the println() statement and put it in another place: right in the middle of the method. Run your program again.
If "I'm still alive" is printed out, you know that the first half of your method runs well, and the problem probably lies in the second half. Repeat the method by placing the println statement in the middle of the second half. If "I'm still alive" is not printed out, the problem lies in the first half. Repeat on the first half. This is called the divide-and-conquer approach, and is a great basic way to start debugging programs (and is how most programmers start out debugging).
Request currentRequest = queue.poll();
System.out.println("Request is "+currentRequest); StringBuffer buf = currentRequest.getMessageBuffer();
System.out.println("Buffer is "+buf); String message = buf.toString();
When we run this excerpt, the output would probably look something like this:
Request is null
NullPointerException:
at SomeClass.someMethod(SomeClass.java:17)
at SomeClass.someParentMethod(SomeClass.java:45)
at SomeClass.main(SomeClass.java:90)
And so, it seems we've located the problem, thanks to handy-dandy println() debugging!
public boolean equals(Object other) {
return this.getWrappedString().equals(((StringWrapper)other).getWrappedString());
}
Looks simple, and in fact it works correctly. Except, it works correctly if it's always passed StringWrapper objects - which may be the case. However, what if we do the following:List myList = new ArrayList();Looks fine (I mean, equals() takes an Object!), but we get something like:
StringWrapper hello = new StringWrapper("hi");
StringWrapper goodbye = new StringWrapper("bye");
//some other code here... myList.add(hello);
//even more code...
myList.add("goodbye");
for(Object object : myList) {
if(hello.equals(object) || goodbye.equals(object))
printGreeting(object);
}
ClassCastException: java.lang.StringFortunately, the ClassCastException message tells us this: "there is an object, whose lowest-level class is that of java.lang.String. You just tried to cast it to something it cannot be cast to. Shame on you." Unfortunately, it does not tell us what you tried to cast it to, nor what variable contains the offending java.lang.String.
at StringWrapper.equals(StringWrapper.java:7)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
public boolean equals(Object other) {
if(other instanceof StringWrapper)
return this.getWrappedString().equals(((StringWrapper)other).getWrappedString());
else
return false;
}
When in doubt with casting, use instanceof.