Java ScreenShot
     

Screenshot Core Java 2: Volume I - Fundamentals

Table of Contents
 11.  Exceptions and Debugging


Some Tips on Using Exceptions

There is a tendency to overuse exceptions. After all, who wants to go to the trouble to write methods that parse input when exception handling makes it so easy? Instead of parsing a URL when the user enters it, just send it off to a method that catches a MalformedURLException. Saves time, saves trouble. Wrong! While having an exception handler costs nothing, the actual handling of an exception will almost always cost a lot of time. Misusing exceptions can therefore slow your code down dramatically. Here are four tips on using exceptions.

  1. Exception handling is not supposed to replace a simple test.

    As an example of this, we wrote some code that uses the built-in Stack class. The code in Example 11-3 tries 1,000,000 times to pop an empty stack. It first does this by finding out whether or not the stack is empty.

    if (!s.empty()) s.pop();
    


    Next, we tell it to pop the stack no matter what. Then, we catch the EmptyStackException that tells us that we should not have done that.

    try()
    {
     s.pop();
    }
    catch (EmptyStackException e)
    {
    }
    


    On our test machine, we got the timing data in Table 11-1.

    Table 11-1. Timing data

    Test

    Throw/Catch

    milliseconds

    milliseconds

    As you can see, it took far longer to catch an exception than it does to perform a simple test. The moral is: Use exceptions for exceptional circumstances only.

  2. Do not micromanage exceptions.

    Many programmers wrap every statement in a separate try block.

    InputStream is;
    Stack s;
    for (i = 0; i < 100; i++)
    {
     try
     {
     n = s.pop();
     }
     catch (EmptyStackException s)
     {
     // stack was empty
     }
     try
     {
     out.writeInt(n);
     }
     catch (IOException e)
     {
     // problem writing to file
     }
    }
    


    This approach blows up your code dramatically. Think about the task that you want the code to accomplish. Here we want to pop 100 numbers off a stack and save them to a file. (Never mind why—it is just a toy example.) There is nothing we can do if a problem rears its ugly head. If the stack is empty, it will not become occupied. If there is an error in the file, the error will not magically go away. It therefore makes sense to wrap the entire task in a try block. If any one operation fails, you can then abandon the task.

    try
    {
     for (i = 0; i < 100; i++)
     {
     n = s.pop();
     out.writeInt(n);
     }
    }
    catch (IOException e)
    {
     // problem writing to file
    }
    catch (EmptyStackException s)
    {
     // stack was empty
    }
    


    This code looks much cleaner. It fulfills one of the promises of exception handling, to separate normal processing from error-handling.

  3. Do not squelch exceptions.

    In Java, there is the tremendous temptation to shut up exceptions. You write a method that calls a method that might throw an exception once a century. The compiler whines because you have not declared the exception in the throws list of your method. You do not want to put it in the throws list because then the compiler will whine about all the methods that call your method. So you just shut it up:

    public Image loadImage(String s)
    {
     try
     {
     lots of code
     }
     catch (Exception e)
     {} // so there
    }
    


    Now your code will compile without a hitch. It will run fine, except when an exception occurs. Then, the exception will be silently ignored. If you believe that exceptions are at all important, you need to make some effort to handle them right.

  4. Propagating exceptions is not a sign of shame.

    Many programmers feel compelled to catch all exceptions that are thrown. If they call a method that throws an exception, such as the FileInputStream constructor or the readLine method, they instinctively catch the exception that may be generated. Often, it is actually better to propagate the exception instead of catching it:

    public void readStuff(String name) throws IOException
    {
     FileInputStream in = new FileInputStream(name);
     . . .
    }
    


    Higher-level methods are often better equipped to inform the user of errors or to abandon unsuccessful commands.

Example 11-3 ExceptionalTest.java
 1. import java.util.*;
 2.
 3. public class ExceptionalTest
 4. {
 5. public static void main(String[] args)
 6. {
 7. int i = 0;
 8. int ntry = 1000000;
 9. Stack s = new Stack();
10. long s1;
11. long s2;
12.
13. // test a stack for emptiness ntry times
14. System.out.println("Testing for empty stack");
15. s1 = new Date().getTime();
16. for (i = 0; i <= ntry; i++)
17. if (!s.empty()) s.pop();
18. s2 = new Date().getTime();
19. System.out.println((s2 - s1) + " milliseconds");
20.
21. // pop an empty stack ntry times and catch the
22. // resulting exception
23. System.out.println("Catching EmptyStackException");
24. s1 = new Date().getTime();
25. for (i = 0; i <= ntry; i++)
26. {
27. try
28. {
29. s.pop();
30. }
31. catch(EmptyStackException e)
32. {
33. }
34. }
35. s2 = new Date().getTime();
36. System.out.println((s2 - s1) + " milliseconds");
37. }
38. }

Java ScreenShot
     
Top
 

Comments