Previous Page TOC Next Page



- 14 -
Compiler Errors and Debugging


After today's lesson, you'll be two-thirds of the way through learning Visual J++. You've covered a lot of topics so far, and today you will learn about compiler errors and debugging. The topic of debugging is fairly advanced, but very important. If you've never used a professional debugger before, today's activity will be extremely valuable. For those of you with more programming experience, you'll have a chance to see how VJ++ handles errors.

When writing applications or applets, it stands to reason that you will have errors. What do you do when a program won't compile; how do you find the error? What do you do when a program does compile, but you get a random program error? Today's lesson will answer these questions. In today's lesson, you will learn:



Today's examples are intentionally written with errors in order to show what happens when errors occur and how to correct them. If you try the examples, remember to write the code exactly as it's stated in this book, even if you recognize the errors. This will help you better understand the logic behind debugging.


Compiler Errors


Compiler errors are errors that prevent your .java files from compiling into .class files. The errors can be wide—ranging from all the appropriate files not being included in the project, to spelling, capitalization, punctuation errors, and so on.

To demonstrate how to deal with compiler errors, create a project workspace for an applet only. Make it multithreaded with no animation. You can name your project TestDebug.

Now, create a resource file and name the dialog DlgButtons. Save the resource file as TestDebug.rct, and name the buttons: ID_HIDE, ID_SHOW, and ID_DESTROY respectively. (See Figure 14.1.)

Figure 14.1. The DlgButtons dialog box.

Type the following code exactly as it is shown. Remember to include the exact punctuation (no matter how obvious an error it might seem).

Add the following code in the header:




import DlgButtons;



DlgButtons m_dlg;



Frame m_Frame;

Remove the existing text in the Init method:




m_dlg = new DlgButtons();



m_dlg.CreateControls();



CreateMyframe();

Now, create a new method called CreateMyFrame:




void CreateMyFrame()



{



    m_Frame = new Frame( "Debug Frame );



    m_Frame.resize(200,200);



    m_Frame.show();



}

Create another new method:




public boolean action(Event evt, Object arg)



{



    String sVal = (String)arg;



    if (evt.target instanceof Button)



    {



        if ( sVal.compareTo("Hide Dialog") == 0)



        {



            if (m_Frame.isShowing())



            {



                 m_Frame.Hide();



            }



            return true;



        }



        else if ( sVal.compareTo("Show Dialog") == 0)



        {



            if (m_Frame.isShowing())



            {



                m_Frame.show();



            }



            return true;



        }



        else if ( sVal.compareTo("Destroy Dialog") == 0)



        {



             if (m_Frame != null)



             {



                 m_Frame.dispose();



                 m_Frame = null;



             }



         }



     }



     return false;



}

Compile the TestDebug project. Remember that the project has errors, so it's not meant to compile. So unless you did something wrong (or right in this case), the first time you attempt to compile this, you will get errors in the Output window. Figure 14.2 shows the Output window as you should see it after you attempt to compile the project.

Figure 14.2. The Output window for the project TestDebug.

Notice that the output information can be very cryptic. But if you double-click any of the lines in the Output window, the offending line in the code window will automatically be displayed.



It is common for a single error to cause multiple lines of compiler errors to be displayed in the Output window. For example, if you incorrectly declared something, such as a class or variable, that was used numerous times throughout the program, this one mistake would cause numerous errors to be reported. Because of this, it is suggested that you always start the debugging process from the top line and work your way down. It is important also to look for patterns or similarities among the error messages to see where they are pointing to in the physical code.

Now we'll demonstrate how VJ++ assists you with finding the errors. Double-click the first error message in the Output window, which is as follows:




C:\VJ++\Projects\TestDebug\TestDebug.java(73,24) : error J0032: Unterminated string constant

Notice that VJ++ uses an arrow to direct your attention to the code within the Source window where the mistake has been made. The cursor is also inserted within the line of code so that the error can be corrected. In this case, your attention is drawn to the following line:




m_Frame = new Frame( "Debug Frame );

The error Unterminated string constant is simple enough. It indicates that a string was not terminated. This might not make sense at first glance because the line ends with the necessary semicolon. However, if you look closer, you will notice that the code has unbalanced quotation marks. Without the terminating quotation mark, the compiler thinks that everything on the line after the D in Debug is part of the string literal. To correct this error, simply add an end quotation mark so that the line will now read:




m_Frame = new Frame( "Debug Frame" );

Now recompile and you will see a completely different list of bugs. This lets you know that the other two (now missing) errors that were listed the first time you compiled were related to the Unterminated string constant error and by fixing the missing quotation mark, both errors disappear. Wow, that's two for one! This happened because the missing quotation mark caused the additional errors to be generated when the compiler was trying to understand how the line of code preceding the quote and following the quote would work together.



When error messages occur, it is best to start at the top and correct the first error, recompile, and see which errors you get the second time around. Sometimes, one error can have an effect on other parts of the code, which will generate additional errors. Newcomers to programming might assume that if there is one error in the code, only one error message would be shown by the compiler, and that the compiler would only display the lines that had actual errors. Unfortunately, compilers have yet to be developed with that level of logic and intelligence. At first, you should follow the fix one error and recompile rule. Once you become familiar with error patterns, you will soon be able to correct multiple errors without having to recompile after each correction.

Figure 14.3 shows the TestDebug project after the second compile.

Figure 14.3. The Output window for TestDebug on the second try.

This time you get an error that Hide is not a member of the class Frame. This problem is that the correct method is hide, not Hide. Remember, capitalization counts!

Fix this error and recompile, and then only two errors remain. This time the errors were not caused by the poor capitalization of Hide. Go ahead and fix these errors. Hint: one of the errors is a spelling error and the other is a bad method call. (Check the DlgButtons class for the correct method format.)

When you are finished, your entire (correct) program should look something like this:




import java.applet.*;



import java.awt.*;



import DlgButtons;



public class TestDebug extends Applet implements Runnable



{



    Thread m_TestDebug = null;



    DlgButtons m_dlg;



    Frame m_Frame;



    public TestDebug()



    {



    }



    public String getAppletInfo()



    {



        return "Name: TestDebug\r\n" +



       "Author: Your Name\r\n" +



       "Created with Microsoft Visual J++ Version 1.0";



    }



    public void init()



    {



         m_dlg = new DlgButtons( this );



         m_dlg.CreateControls();



         CreateMyFrame();



     }



     public void destroy()



    {



    }



    public void paint(Graphics g)



    {



    }



    public void start()



    {



         if (m_TestDebug == null)



         {



             m_TestDebug = new Thread(this);



             m_TestDebug.start();



          }



    }



    public void stop()



   {



       if  (m_TestDebug != null)



       {



            m_TestDebug.stop();



            m_TestDebug = null;



        }



    }



     public void run()



     {



           while (true)



            {



               try



               {



                   repaint();



                    Thread.sleep(50);



               }



               catch (InterruptedException e)



               {



                   stop();



               }



           }



     }



     void CreateMyFrame()



     {



          m_Frame = new Frame( "Debug Frame" );



          m_Frame.resize(200,200);



          m_Frame.show();



     }



    public boolean action(Event evt, Object arg)



    {



         String sVal = (String)arg;



         if (evt.target instanceof Button)



          {



              if ( sVal.compareTo("Hide Dialog") == 0)



              {



                   if (m_Frame.isShowing())



                   {



                       m_Frame.hide();



                   }



                   return true;



              }



              else if ( sVal.compareTo("Show Dialog") == 0)



              {



                   if (m_Frame.isShowing())



                   {



                        m_Frame.show();



                   }



                   return true;



               }



               else if ( sVal.compareTo("Destroy Dialog") == 0)



               {



                    if (m_Frame != null)



                    {



                         m_Frame.dispose();



                         m_Frame = null;



                    }



               }



          }



          return false;



     }

Now your program compiles, but will it run? If you typed everything in correctly, your program will run. It won't "blow up," but there will still be some problems. To help determine what these problems are, you can use Microsoft Developer Studio debugger tools.

Microsoft Developer Studio Debugger


When debugging your program, you will typically use breakpoints to halt the execution of your program. To enable a breakpoint on a line of code, set your cursor on the line and press F9. To use advanced features of breakpoints such as conditional breakpoints and breakpoints that are skipped the first n number of times, press ALT+F9 or select Edit | Breakpoints from the menu. Figure 14.4 is an example of the Breakpoints dialog box.

Figure 14.4. An example of the Breakpoints dialog box.

Once you have breakpoints defined in your applet or application, you start the debugging process by either pressing F5 or by using the Build | Debug menu command. You also need to make sure your program was compiled using the Java Virtual Machine Debug setting; debug cannot debug a non-debug version (Java Virtual Machine Release) of your program.

Up until now, you've been running your programs using Build | Execute—stop it! That's bad. . .very bad in this chapter. Even if you select Build | Execute (CTRL-F5) using a debug version of your program, the program will not run in debug mode; you must use Build | Debug (F5).

Debugging Your Applet


Now take your TestDebug applet and run it to see what works and what doesn't work. Go ahead and run your applet. You should get output similar to Figure 14.5.

Figure 14.5. The TestDebug applet running in Internet Explorer.

As soon as you click any of the buttons on the page, no matter which button is pressed, the Frame window goes away and will not display again. Now, we'll analyze this. The window displays, so that section of code is probably correct. The window hides (or destroys) so the code is working, but not properly. And, the Show event is not being recalled, so it's not working either. You now need to step through the code to discover the problems one at a time.

In your program, go to the following segment of code:




public boolean action(Event evt, Object arg)



{



    String sVal = (String)arg;



    if (evt.target instanceof Button)



    {



        if ( sVal.compareTo("Hide Dialog") == 0)

Because you could probably assume that the problem with the Window not reappearing when you hit the Show Dialog button lies in the action method, you should start there. Highlight the first if line and set a breakpoint. A red dot will appear on the left margin that indicates a breakpoint is set on this line. Your code window should look something like Figure 14.6.



The red dot that appears on the left margin in your program is one of the many color coding features that Microsoft Developer Studio uses to identify certain items. Remember, if you change the default Windows system colors, the colors that are displayed will change.

Figure 14.6. The Code for TestDebug showing a breakpoint set.

Now start the debugger by pressing F5.

Your program will start, the browser will be displayed containing the applet and its three buttons on the page, and the Dialog window will be displayed. To get the program to the state where it doesn't work right, click Hide Dialog.

After you click Hide Dialog, Microsoft Developer Studio will return to the foreground and the cursor will be placed on the line where the breakpoint was set. This is nifty, huh? But, you know this line is fine; it's just your starting point for stepping through the debug session. For now, press F5, or select Debug | Go from the menu. Use Ctrl+Esc to get back to the browser. Now click Show Dialog. You will once again be returned to the action method in the code window.

Now you will want to step through the code to find out why the Dialog is not being displayed properly.

From the View menu, select Watch and then select Variables to display both the Watch and Variables windows.

The Watch window displays variables (both objects and non-objects). You define the variables you want to watch by adding them to the Watch window. The Variables window displays all variables that the current line affects or that were affected by the previous line of code. If you arrange your Variables window on the bottom left of your screen and the Watch window on the bottom right, your window should now resemble Figure 14.7.

Figure 14.7. Microsoft Developer Studio with Variable and Watch windows displayed.

Now to step through the code. You use the Step Into, Step Over, and Step Out debugging commands from the menu or their hot keys F10, F11, Shift+F11 respectively to move through the code. For your purposes, you will be using only Step Into (F10).



If this is the first professional debugger that you have used, Step Into, Step Over, and Step Out can be somewhat confusing. Step Into is used when you want to follow the code into the selected method. Step Over is used when you want to remain in the current method. Step Out is used when you want to stop following the code in the current method.

Step to the next line of code. You should be at the following:




if ( sVal.compareTo("Hide Dialog") == 0)

Now step two more times and you should be at the following:




if (m_Frame.isShowing())

At this point, you should be in the right function, so now see what the program does next. Step once more with F10. Oops, suddenly you are on the return true; line. What happened? Why was the Frame.show() line skipped? Go back and try to figure out what went wrong.

Look at the following code fragment and think about what the if statement is asking:




if (m_Frame.isShowing())



{



   m_Frame.show();



}



return true;

You could equate the preceding code fragment to the following pseudo-code:




If the m_Frame is showing now, execute the code m_Frame.show().

Are you curious? Why show something that's already showing? But, more importantly, the code only executes when m_Frame is showing, not when it is not showing, which is not the correct purpose of the test.

If you simply change the line to read if (!m_Frame.isShowing()), the code will make sense because it will show the frame when m_Frame is not showing.

So now that you have found that logic error, what do you do? Simply let your program finish gracefully by pressing Debug | Go from the menu and then closing your browser. If this doesn't work, select Debug | Stop Debugging from the menu. Go ahead and make the change, recompile your program, and rerun the applet.

When you run your program now, there still seems to be a problem. For some reason, when you click the Destroy Dialog button, you are never able to show the dialog again. Using what you've learned about debugging, setting breakpoints, and stepping through your code, see if you can find a solution to make the Show Dialog button work properly once the Destroy Dialog button has been clicked. The following is the corrected Source Code segment:




void CreateMyFrame()



{



    else if ( sVal.compareTo("Show Dialog") == 0)



    {



        if (m_Frame == null)



        {



            CreateMyFrame();



        }



        else if (!m_Frame.isShowing())



        {



            m_Frame.show();



        }



    return true;



    }



}

Summary


Today you learned to use the Microsoft Developer Studio Integrated Debugger. You experimented with the two ways to use the debugger: at compile time to check compiler errors, which consist mainly of syntax or improper usage errors, and at runtime to check logic errors, which consist mainly of faulty construction. Today we have shown you most of the tools and the most common methods of debugging programs.

Debugging code has never been a simple undertaking. There are so many different kinds of errors that can occur, ranging from simple typing errors to major logic errors. However, as you've seen, Visual J++ provides you with a wide variety of tools to help you debug your programs. The more you use Visual J++, the more comfortable you will become with the debugger.

Learning how to debug is necessary, but debugging should not be the only tool in your tool chest. Careful planning and execution go much further in helping to create solid code than a debugger ever will.

Regardless of whether you take the preceding advice to heart or not, remember that the manner in which you view your classes, the logic you use to write them, and the methods that you use to debug classes are personal choices and preferences.

Q&A


Q: It seems like there are an awful lot of places I can do things wrong. What would be good methodology to help me to produce bug-free programs?

A: I wish I had an answer for you; this has been the goal of programmers for years. From past experience, the single most important element in producing logic error-free programs is to design the class hierarchies and program flows before writing any code.

The best advice for creating typo and syntax error-free programs is to use the abilities of the IDE you are using. When using Microsoft Developer Studio, for example, the cursor will automatically jump to the correct tab (indent level) based upon how you terminate the previous line. If you don't terminate a line with a semicolon, Microsoft Developer Studio will automatically indent the next line. This should be a clue to you. If Microsoft Developer Studio indents a line that should not be indented, the chances are pretty good that there is a syntax error in the preceding line.

Microsoft Developer Studio also color codes certain types of text such as keywords and comments. These colors are configurable by selecting Tools | Options from the menu and modifying the information under the Format tab.

Q: As I am debugging my program, suppose I want to see the value of a variable that is not on the current affected line of code. Because I just want to check the value of this variable once, I also do not want to add it to the Watch window. How do I do this?

A: As with most new Microsoft products these days, there is a great deal of functionality available by using the right mouse button. If you click on a variable with the right mouse button, a rather large pop-up window appears allowing you to jump to all sorts of other windows. It just so happens that one of the options on this pop-up is QuickWatch xxx (where xxx is the name of the variable you clicked). This action will bring up a detailed window (similar to a Watch window) that displays all data about the variable you clicked on.

Q: What's the difference between Debug and Release mode? When do I use each?

A: Typically, you would compile your program as a Debug version while you are developing your project. You must use the Debug version when you need to step through your code and check for variable values or for logic errors. Because all of the Debug-related information is stored in this version's file, the Debug version is larger and less efficient. Therefore, when you are satisfied with your program and its behavior, you would then compile a Release version for General Distribution.

Previous Page Page Top TOC Next Page