Java ScreenShot
     

Screenshot Core Java 2: Volume I - Fundamentals

Table of Contents
 11.  Exceptions and Debugging


Using a Debugger

Debugging with print statements is not one of life's more joyful experiences. You constantly find yourself adding and removing the statements, then recompiling the program. Using a debugger is better because a debugger runs your program in full motion until it reaches a breakpoint, and then you can look at everything that interests you.

The JDB Debugger

The SDK includes JDB, an extremely rudimentary command-line debugger. Its user interface is so minimal that you will not want to use it except as a last resort. It really is more a proof of concept than a usable tool. We nevertheless give a brief introduction because there are situations in which it is better than no debugger at all. Of course, many Java coding environments have far more convenient debuggers. The main principles of all debuggers are the same, and you may want to use the example in this section to learn to use the debugger in your environment instead of JDB. Examples 11-10 through 11-12 show a deliberately corrupted version of the ButtonTest program from . (We broke up the program and placed each class into a separate file because some debuggers have trouble dealing with multiple classes in the same file.) When you click on any of the buttons, nothing happens. Look at the source code—button clicks are supposed to set the background color to the color specified by the button name.

Example 11-10 BuggyButtonTest.java
 1. import javax.swing.*;
 2.
 3. public class BuggyButtonTest
 4. {
 5. public static void main(String[] args)
 6. {
 7. BuggyButtonFrame frame = new BuggyButtonFrame();
 8. frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 9. frame.show();
10. }
11. }


Example 11-11 BuggyButtonFrame.java
 1. import java.awt.*;
 2. import javax.swing.*;
 3.
 4. public class BuggyButtonFrame extends JFrame
 5. {
 6. public BuggyButtonFrame()
 7. {
 8. setTitle("BuggyButtonTest");
 9. setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
10.
11. // add panel to frame
12.
13. BuggyButtonPanel panel = new BuggyButtonPanel();
14. Container contentPane = getContentPane();
15. contentPane.add(panel);
16. }
17.
18. public static final int DEFAULT_WIDTH = 300;
19. public static final int DEFAULT_HEIGHT = 200;
20. }


Example 11-12 BuggyButtonPanel.java
 1. import java.awt.*;
 2. import java.awt.event.*;
 3. import javax.swing.*;
 4.
 5. class BuggyButtonPanel extends JPanel
 6. {
 7. public BuggyButtonPanel()
 8. {
 9. ActionListener listener = new ButtonListener();
10.
11. JButton yellowButton = new JButton("Yellow");
12. add(yellowButton);
13. yellowButton.addActionListener(listener);
14.
15. JButton blueButton = new JButton("Blue");
16. add(blueButton);
17. blueButton.addActionListener(listener);
18.
19. JButton redButton = new JButton("Red");
20. add(redButton);
21. redButton.addActionListener(listener);
22. }
23.
24. private class ButtonListener implements ActionListener
25. {
26. public void actionPerformed(ActionEvent event)
27. {
28. String arg = event.getActionCommand();
29. if (arg.equals("yellow"))
30. setBackground(Color.yellow);
31. else if (arg.equals("blue"))
32. setBackground(Color.blue);
33. else if (arg.equals("red"))
34. setBackground(Color.red);
35. }
36. }
37. }


In a program this short, you may be able to find the bug just by reading the source code. Let us pretend that this was so complicated a program that reading the source code is not practical. Here is how you can run the debugger to locate the error. To use JDB, you must first compile your program with the -g option, for example:

javac -g BuggyButtonTest.java BuggyButtonFrame.java
 BuggyButtonPanel.java


When you compile with this option, the compiler adds the names of local variables and other debugging information into the class files. Then you launch the debugger:

jdb BuggyButtonTest


Once you launch the debugger, you will see a display that looks something like this:

Initializing jdb...
>


The > prompt indicates the debugger is waiting for a command. Table 11-5 shows all the debugger commands. Items enclosed in [...] are optional, and the suffix (s) means that you can supply one or more arguments separated by spaces.

Table 11-5. Debugging commands

threads [threadgroup]

Lists threads

thread thread_id

Sets default thread

suspend [thread_id(s)]

Suspends threads (default: all)

resume [thread_id(s)]

Resumes threads (default: all)

where [thread_id] or all

Dumps a thread's stack

wherei [thread_id] or all

Dumps a thread's stack and program counter info

threadgroups

Lists threadgroups

threadgroup name

Sets current threadgroup

print name(s)

Prints object or field

dump name(s)

Prints all object information

locals

Prints all current local variables

classes

Lists currently known classes

methods class

Lists a class's methods

stop in class.method

Sets a breakpoint in a method

stop at class:line

Sets a breakpoint at a line

up [n]

Moves up a thread's stack

down [n]

Moves down a thread's stack

clear class:line

Clears a breakpoint

step

Executes the current line, stepping inside calls

stepi

Executes the current instruction

step up

Executes until the end of the current method

next

Executes the current line, stepping over calls

cont

Continues execution from breakpoint

catch class

Breaks for the specified exception

ignore class

Ignores the specified exception

list [line]

Prints source code

use [path]

Displays or changes the source path

memory

Reports memory usage

gc

Frees unused objects

load class

Loads Java class to be debugged

run [class [args]]

Starts execution of a loaded Java class

!!

Repeats last command

help (or ?)

Lists commands

exit (or quit)

Exits debugger

We will cover only the most useful JDB commands in this section. The basic idea, though, is simple: you set one or more breakpoints, then run the program. When the program reaches one of the breakpoints you set, it stops. Then, you can inspect the values of the local variables to see if they are what they are supposed to be. To set a breakpoint, use the
stop in class.method


or

stop at class:line


command. For example, let us set a breakpoint in the actionPerformed method of BuggyButtonTest. To do this, enter:

stop in BuggyButtonPanel$ButtonListener.actionPerformed


Now we want to run the program up to the breakpoint, so enter:

run


The program will run, but the breakpoint won't be hit until Java starts processing code in the actionPerformed method. For this, click on the Yellow button. The debugger breaks at the start of the actionPerformed method. You'll see:

[View full width]
Breakpoint hit: thread="AWT-EventQueue-0", BuggyButtonPanel$ButtonListener.
Java graphics ccc.gifactionPerformed(), line=28, bci=0
 28 String arg = event.getActionCommand();


Because the debugger does not give you a window with the current source line showing, it is easy to lose track of where you are; the list command lets you find out where you are. While the program is stopped after you enter list, the debugger will show you the current line and a couple of the lines above and below. You also see the line numbers. For example:

24 private class ButtonListener implements ActionListener
25 {
26 public void actionPerformed(ActionEvent event)
27 {
28=> String arg = event.getActionCommand();
29 if (arg.equals("yellow"))
30 setBackground(Color.yellow);
31 else if (arg.equals("blue"))
32 setBackground(Color.blue);
33 else if (arg.equals("red"))


Type locals to see all local variables. For example:

Method arguments:
 event = instance of java.awt.event.ActionEvent(id=698)
Local variables:


For more detail, use:

dump variable


For example,

dump event


displays all instance fields of the evt variable.

event = instance of java.awt.event.ActionEvent(id=698) {
 SHIFT_MASK: 1
 CTRL_MASK: 2
 META_MASK: 4
 ALT_MASK: 8
 ACTION_FIRST: 1001
 ACTION_LAST: 1001
 ACTION_PERFORMED: 1001
 actionCommand: "Yellow"
 modifiers: 0
 serialVersionUID: -7671078796273832149
 . . .


There are two basic commands to single-step through a program. The step command steps into every method call. The next command goes to the next line without stepping inside any further method calls. Type next twice and then type list to see where you are. The program stops in line 31.

27 {
28 String arg = event.getActionCommand();
29 if (arg.equals("yellow"))
30 setBackground(Color.yellow);
31=> else if (arg.equals("blue"))
32 setBackground(Color.blue);
33 else if (arg.equals("red"))
34 setBackground(Color.red);
35 repaint();
36 }


That is not what should have happened. It was supposed to call setColor(Color.yellow) and then go to the repaint command. Dump the arg variable.

arg = "Yellow"


Now you can see what happened. The value of arg was "Yellow", with an uppercase Y, but the comparison tested

if (arg.equals("yellow"))


with a lowercase y. Mystery solved. To quit the debugger, type:

quit


As you can see from this example, the debugger can be used to find an error, but the command-line interface is very inconvenient. Remember to use list and locals whenever you are confused about where you are. But if you have any choice at all, use a better debugger for serious debugging work.

The Sun ONE Studio Debugger

Sun ONE Studio has a modern and convenient debugger that has many of the amenities that you would expect. In particular, you can set breakpoints, inspect variables, and single-step through a program. To set a breakpoint, move the cursor to the desired line and select Debug -> Add Breakpoint from the menu. The breakpoint line is highlighted (see Screenshot-6).

Screenshot-6. Breakpoints in the Sun ONE Studio debugger

Java graphics 11fig06.jpg


To start debugging, select Debug -> Start from the menu. The program starts running. Set a breakpoint in the first line of the actionPerformed method. When the debugger stops at a breakpoint, you can see the call stack and the local variables (see Screenshot-7).

Screenshot-7. Stopping at a Breakpoint

Java graphics 11fig07.jpg


To single-step through the app, use the Debug -> Step over (F8) or Debug -> Step into (F7) commands. In our example, press the F8 key twice to see how the program skips over the setBackground(Color.yellow) command. Then watch the value of args to see the reason. As you can see, the Sun ONE Studio debugger is much easier to use than JDB because you have visual feedback to indicate where you are in the program. Setting breakpoints and inspecting variables is also much easier. This is typical of debuggers that are a part of an integrated development environment. This chapter introduced you to exception handling and gave you some useful hints for testing and debugging. The final chapter of this tutorial covers the stream, reader, writer, and file classes that you need to program input and output.

Screenshot

Java ScreenShot
     
Top
 

Comments