From using jconsole to monitoring uncaught exceptions, here are a dozen tips that may be worth trying before you launch your favorite IDE’s debugger.
Download a PDF of this article
[This article on Java debugging is adapted from Core Java Volume I: Fundamentals, 12th Edition, by Cay S. Horstmann, published by Oracle Press. —Ed.]
Suppose you wrote your Java program and made it bulletproof by catching and properly handling all the exceptions. Then you run it, and it does not work correctly.
Now what? (If you never have this problem, you can skip this article.)
Of course, it is best if you have a convenient and powerful debugger, and debuggers are available as a part of IDEs. That said, here are a dozen tips worth trying before you launch your IDE’s debugger.
Tip 1. You can print or log the value of any variable with code like the following:
System.out.println("x=" + x);
or
Logger.getGlobal().info("x=" + x);
If x is a number, it is converted to its string equivalent. If x is an object, Java calls its toString method. To get the state of the implicit parameter object, print the state of the this object.
Logger.getGlobal().info("this=" + this);
Most of the classes in the Java library are very conscientious about overriding the toString method to give you useful information about the class. This is a real boon for debugging. You should make the same effort in your classes.
Tip 2. One seemingly little-known but very useful trick is putting a separate main method in each class. Inside it, you can put a unit test stub that lets you test the class in isolation.
public class MyClass
{
// the methods and fields
. . .
public static void main(String[] args)
{
// the test code
}
}
Make a few objects, call all methods, and check that each of them does the right thing. You can leave all these main methods in place and launch the Java Virtual Machine separately on each of the files to run the tests.
When you run an applet, none of these main methods are ever called.
When you run an application, the JVM calls only the main method of the startup class.
Tip 3. If you liked the preceding tip, you should check out JUnit. JUnit is a very popular unit testing framework that makes it easy to organize suites of test cases.
Run the tests whenever you make changes to a class, and add another test case whenever you find a bug.
Tip 4. A logging proxy is an object of a subclass that intercepts method calls, logs them, and then calls the superclass. For example, if you have trouble with the nextDouble method of the Random class, you can create a proxy object as an instance of an anonymous subclass, as follows:
var generator = new Random()
{
public double nextDouble()
{
double result = super.nextDouble();
Logger.getGlobal().info("nextDouble: "
+ result);
return result;
}
};
Whenever the nextDouble method is called, a log message is generated.
To find out who called the method, generate a stack trace.
Tip 5. You can get a stack trace from any exception object by using the printStackTrace method in the Throwable class. The following code catches any exception, prints the exception object and the stack trace, and rethrows the exception so it can find its intended handler:
try
{
. . .
}
catch (Throwable t)
{
t.printStackTrace();
throw t;
}
You don’t even need to catch an exception to generate a stack trace. Simply insert the following statement anywhere into your code to get a stack trace:
Thread.dumpStack();
Tip 6. Normally, the stack trace is displayed on System.err. If you want to log or display the stack trace, here is how you can capture it into a string.
var out = new StringWriter();
new Throwable().printStackTrace(new PrintWriter(out));
String description = out.toString();
Tip 7. It is often handy to trap program errors in a file. However, errors are sent to System.err, not System.out. Therefore, you cannot simply trap them by running
java MyProgram > errors.txt
Instead, capture the error stream as
java MyProgram 2> errors.txt
To capture both System.err and System.out in the same file, use
java MyProgram 1> errors.txt 2>&1
This works in bash and in the Windows shell.
Tip 8. Having the stack traces of uncaught exceptions show up in System.err is not ideal. These messages are confusing to end users if they happen to see them, and they are not available for diagnostic purposes when you need them.
A better approach is to log the uncaught exceptions to a file. You can change the handler for uncaught exceptions with the static Thread.setDefaultUncaughtExceptionHandler method.
Thread.setDefaultUncaughtExceptionHandler(
new Thread.UncaughtExceptionHandler()
{
public void uncaughtException(Thread t,
Throwable e)
{
// save information in log file
};
});
Tip 9. To watch classes loading, launch the JVM with the -verbose flag. You will get a printout such as in Figure 1.
0 comments:
Post a Comment