Debugging
Debugging an application involves trying to determine the state of the application at different stages during its execution. Debugging is essentially an "intrusive" activity that probes the condition of the application. For developers, debugging is a means of verifying that the application is running properly. There are different ways to carry out the debugging of an application. Because you will be focusing on writing Java applications, let's cover techniques relevant to those applications.
When the Java Development Kit was introduced, there were no IDEs available for writing source code. IDE, as the name connotes, is a full-fledged integrated development environment that provides support not only for writing formatted source code but also for allied activities like compiling, executing, and debugging applications. IDEs come with sophisticated debugging functions such as setting break points, inspecting contents of variables, putting a watch on variables, changing the flow of execution from the "normal" flow defined in the source code, and so on.
Where an IDE is not available or the use of an IDE is not possible, the simplest "low tech" way to perform debugging is by printing out the contents of the variable or data on the standard output console. In Java applications you can do this by introducing System.out.println statements in the source code. An example of a debug statement that shows what exception has occurred is given here:
catch(Exception e) { System.out.println("***************"); System.out.println("Exception occurred in method m1()." + e.getMessage()); System.out.println("***************"); }
This code snippet shows that the application displays a message identifying where the message occurred and the details of the exception that occurred. The debug message is written to the stdout and therefore is displayed on the console.
The printStackTrace() Method
The printStackTrace() method is another feature that can be used to debug Java applications when an exception occurs. The printStackTrace() method is an overloaded method and can be used in different ways. The simplest form is to invoke the printStackTrace() method of the Exception object without any parameters, as follows:
catch(Exception e) { System.out.println("***************"); System.out.println("Exception occurred in method m1()." + e.getMessage()); e.printStackTrace(); System.out.println("***************"); }
Alternatively, if the stack trace needs to be written to persistent storage, such as a file, you can use printStackTrace() with either a PrintWriter or PrintStream object passed as a parameter. The printStackTrace() method can use either of these objects to write the stack trace data.
In addition to simple print statements, you can use more sophisticated techniques, such as introducing severity levels, along with the debug messages. You can define the critical points in the application, which can be assigned different severity levels. The severity level can then be printed out, which enables application users to understand how serious the problem is. This helps other applications that may be, for example, monitoring the health of your application to determine the severity of the error in order to perform any notifications (or any other actions) as required.
The downside of introducing debug statements in your Java code is that these remain in the executablethat is, the .class filewhen your source code is compiled. Unlike Java, C/C++ languages have powerful compile time preprocessors that can be used to keep debug statements embedded in the executable file until they are no longer required (for example, at the end of development). The debug statements can be turned on and off by changing a compile time preprocessor directive. If you need to have similar switch-on/switch-off capability for your debug statements, you can do it by using a static variable as a flag. Before executing any debug statements, you should check for the state of the flag in your source code. If the state of the flag is true, you can execute your debug statements; otherwise, you can skip execution.
The value of the debug flag can be set by reading a property using the Properties class. The property itself can be set and passed to the JVM while executing the Java application.
Here's an example:
class MyDebugHelper { public static int WARNING = 0; public static int LOW = 1; public static int MEDIUM = 2; public static int HIGH = 3; public static void debug(int severityLevel, Object inpObj, String message) { System.out.println("*******************************"); System.out.println("DEBUG for " + inpObj); System.out.println("Severity Level: " + severityLevel); System.out.println("Message: " + message); System.out.println("*******************************"); } } class MyApp { public boolean debugFlag = false; MyApp() { // set the debug flag Properties inputProp = new Properties(); debugFlag = Boolean.getBoolean(inputProp.getProperty("DEBUG_FLAG")); } public void myMethod1() { ... // perform processing if(debugFlag) { -MyDebugHelper.debug(MyDebugHelper.MEDIUM, inpData, "Incorrect value passed as input in inpData variable!"); } }
This code snippet shows the debug class MyDebugHelper, which provides the necessary debug functionality to any application. The method debug() of the MydebugHelper class is the actual method that will be used by any application for introducing debug statements. The MyDebugHelper class has four levels for the severity of the error: LOW, MEDIUM, HIGH, and WARNING. The debug() method takes the severity level, an input object (to display the contents of the object), and a message as input parameters sent by any application performing debugging. The parameters are then displayed in a neatly formatted output on the console.
Take a look at the application class MyApp. The constructor of the MyApp class reads the property DEBUG_FLAG and sets the Boolean value of the property to the debugFlag instance variable. The getBoolean() method of the Boolean class is used to return a Boolean true value if there is a property DEBUG_FLAG with a string value true set. Otherwise, the getBoolean() returns a Boolean false value. After the appropriate initialization of the debugFlag variable, the debugFlag variable can be used anywhere in the class to switch on or off debug statements in the class. An example of this is in the myMethod1() method of the MyApp class.
How do you set the value of the DEBUG_FLAG property? You could do this simply by passing the DEBUG_FLAG as a property while executing the class MyApp, as follows:
java DDEBUG_FLAG=true MyApp
The D option, as the JDK documentation states, can be used to pass any properties to classes loaded in a JVM.
The debugging technique of displaying messages on the console is the easiest to implement. Developers can identify the cause of the exception and where it occurred by studying the debug messages that appear on the console. The downside of this technique is that it is too simplistic and not very practical when there are a lot of messages displayed on the console. Moreover, because these are displayed on the console, a history of the previous messages is not available. To get around this, you can redirect the output generated on the console to a file. You will learn more about this in the logging section later today.