You now know how to throw an exception. It is pretty easy. You throw it and you forget it. Of course, some code has to catch the exception. Catching exceptions requires more planning. If an exception occurs that is not caught anywhere, the program will terminate and print a message to the console, giving the type of the exception and a stack trace. Graphics programs (both applets and applications) catch exceptions, print stack trace messages, and then go back to the user interface processing loop. (When you are debugging a graphically based program, it is a good idea to keep the console available on the screen and not minimized.) To catch an exception, you set up a try/catch block. The simplest form of the try block is as follows:
If any of the code inside the try block throws an exception of the class specified in the catch clause, then
If none of the code inside the TRy block throws an exception, then the program skips the catch clause. If any of the code in a method throws an exception of a type other than the one named in the catch clause, this method exits immediately. (Hopefully, one of its callers has already coded a catch clause for that type.) To show this at work, we show some fairly typical code for reading in text:
Notice that most of the code in the try clause is straightforward: it reads and processes lines until we encounter the end of the file. As you can see by looking at the Java API, there is the possibility that the read method will throw an IOException. In that case, we skip out of the entire while loop, enter the catch clause and generate a stack trace. For a toy program, that seems like a reasonable way to deal with this exception. What other choice do you have? Often, the best choice is to do nothing at all and simply pass the exception on to the caller. If an error occurs in the read method, let the caller of the read method worry about it! If we take that approach, then we have to advertise the fact that the method may throw an IOException.
Remember, the compiler strictly enforces the throws specifiers. If you call a method that throws a checked exception, you must either handle it or pass it on. Which of the two is better? As a general rule, you should catch those exceptions that you know how to handle and propagate those that you do not know how to handle. When you propagate an exception, you must add a throws specifier to alert the caller that an exception may be thrown. Look at the Java API documentation to see what methods throw which exceptions. Then decide whether you should handle them or add them to the throws list. There is nothing embarrassing about the latter choice. It is better to direct an exception to a competent handler than to squelch it. Please keep in mind that there is one exception to this rule, as we mentioned earlier. If you are writing a method that overrides a superclass method that throws no exceptions (such as paintComponent in JComponent), then you must catch each checked exception in the method's code. You are not allowed to add more throws specifiers to a subclass method than are present in the superclass method. C++ NOTE
Catching Multiple ExceptionsYou can catch multiple exception types in a TRy block and handle each type differently. You use a separate catch clause for each type as in the following example:
The exception object (e1, e2, e3) may contain information about the nature of the exception. To find out more about the object, try e3.getMessage() to get the detailed error message (if there is one), or e3.getClass().getName() to get the actual type of the exception object. Rethrowing and Chaining ExceptionsYou can throw an exception in a catch clause. Typically, you do this because you want to change the exception type. If you build a subsystem that other programmers use, it makes a lot of sense to use an exception type that indicates a failure of the subsystem. An example of such an exception type is the ServletException. The code that executes a servlet may not want to know in minute detail what went wrong, but it definitely wants to know that the servlet was at fault. Here is how you can catch an exception and rethrow it.
Here, the ServletException is constructed with the message text of the exception. As of JDK 1.4, you can do better than that and set the original exception as the "cause" of the new exception:
When the exception is caught, the original exception can be retrieved: Throwable e = se.getCause(); This wrapping technique is highly recommended. It allows you to throw high-level exceptions in subsystems without losing the details of the original failure. TIP
NOTE
The finally ClauseWhen your code throws an exception, it stops processing the remaining code in your method and exits the method. This is a problem if the method has acquired some local resource that only it knows about and if that resource must be cleaned up. One solution is to catch and rethrow all exceptions. But this solution is tedious because you need to clean up the resource allocation in two places, in the normal code and in the exception code. Java has a better solution, the finally clause. Here we show you how to properly dispose of a Graphics object. If you do any database programming in Java, you will need to use the same techniques to close connections to the database. As you will see in Chapter 4 of Volume 2, it is very important to close all database connections properly, even when exceptions occur. The code in the finally clause executes whether or not an exception was caught. In the following example, the program will dispose of the graphics context under all circumstances.
Let us look at the three possible situations in which the program will execute the finally clause.
You can use the finally clause without a catch clause. For example, consider the following TRy statement:
The g.dispose() command in the finally clause is executed whether or not an exception is encountered in the try block. Of course, if an exception is encountered, it is rethrown and must be caught in another catch clause. In fact, as explained in the following tip, we think it is a very good idea to use the finally clause in this way. TIP
CAUTION
Sometimes the finally clause gives you grief, namely if the cleanup method can also throw an exception. A typical case is closing a stream. (See Chapter 12 for more information on streams.) Suppose you want to make sure that you close a stream when an exception hits in the stream processing code.
Now suppose that the code in the try block throws some exception other than an IOException that is of interest to the caller of the code. The finally block executes, and the close method is called. That method can itself throw an IOException! When it does, then the original exception is lost and the IOException is thrown instead. That is very much against the spirit of exception handling. It is always a good idea unfortunately not one that the designers of the InputStream class chose to follow to throw no exceptions in cleanup operations such as dispose, close, and so on, that you expect users to call in finally blocks. C++ NOTE
Analyzing Stack Trace ElementsA stack trace is a listing of all pending method calls at a particular point in the execution of a program. You have almost certainly seen stack trace listings they are displayed whenever a Java program terminates with an uncaught exception. NOTE
Before JDK 1.4, you could access the text description of a stack trace by calling the printStackTrace method of the THRowable class. Now you can call the getStackTrace method to get an array of StackTraceElement objects that you can analyze in your program. For example,
The StackTraceElement class has methods to obtain the file name and line number, as well as the class and method name, of the executing line of code. The toString method yields a formatted string containing all of this information. JDK 5.0 adds the static Thread.getAllStackTraces method that yields the stack traces of all threads. Here is how you use that method:
See Volume 2 for more information on threads and the Map interface. Example 11-1 prints the stack trace of a recursive factorial function. For example, if you compute factorial(3), the printout is factorial(3): StackTraceTest.factorial(StackTraceTest.java:8) StackTraceTest.main(StackTraceTest.java:23) factorial(2): StackTraceTest.factorial(StackTraceTest.java:8) StackTraceTest.factorial(StackTraceTest.java:14) StackTraceTest.main(StackTraceTest.java:23) factorial(1): StackTraceTest.factorial(StackTraceTest.java:8) StackTraceTest.factorial(StackTraceTest.java:14) StackTraceTest.factorial(StackTraceTest.java:14) StackTraceTest.main(StackTraceTest.java:23) return 1 return 2 return 6 Example 11-1. StackTraceTest.java1. import java.util.*; 2. 3. /** 4. A program that displays a trace feature of a recursive 5. method call. 6. */ 7. public class StackTraceTest 8. { 9. /** 10. Computes the factorial of a number 11. @param n a nonnegative integer 12. @return n! = 1 * 2 * . . . * n 13. */ 14. public static int factorial(int n) 15. { 16. System.out.println("factorial(" + n + "):"); 17. Throwable t = new Throwable(); 18. StackTraceElement[] frames = t.getStackTrace(); 19. for (StackTraceElement f : frames) 20. System.out.println(f); 21. int r; 22. if (n <= 1) r = 1; 23. else r = n * factorial(n - 1); 24. System.out.println("return " + r); 25. return r; 26. } 27. 28. public static void main(String[] args) 29. { 30. Scanner in = new Scanner(System.in); 31. System.out.print("Enter n: "); 32. int n = in.nextInt(); 33. factorial(n); 34. } 35. } java.lang.Throwable 1.0
java.lang.Exception 1.0
java.lang.RuntimeException 1.0
java.lang.StackTraceElement 1.4
Taking a Final Look at Java Error and Exception HandlingExample 11-2 deliberately generates a number of different errors and catches various exceptions (see Figure 11-2). Figure 11-2. A program that generates exceptionsTry it out. Click on the buttons and see what exceptions are thrown. As you know, a programmer error such as a bad array index throws a RuntimeException. An attempt to open a nonexistent file triggers an IOException. Perhaps surprisingly, floating-point computations such as dividing by 0.0 or taking the square root of -1 do not generate exceptions. Instead, they yield the special "infinity" and "not a number" values that we discussed in Chapter 3. However, integer division by 0 throws an ArithmeticException. We trap the exceptions that the actionPerformed methods throw in the fireActionPerformed method of the radio buttons and display them in the text field. However, the actionPerformed method is declared to throw no checked exceptions. Thus, the handler for the "No such file" button must catch the IOException. If you click on the "Throw unknown" button, an UnknownError object is thrown. This is not a subclass of Exception, so our program does not catch it. Instead, the user interface code prints an error message and a stack trace to the console. Example 11-2. ExceptTest.java1. import java.awt.*; 2. import java.awt.event.*; 3. import javax.swing.*; 4. import java.io.*; 5. 6. public class ExceptTest 7. { 8. public static void main(String[] args) 9. { 10. ExceptTestFrame frame = new ExceptTestFrame(); 11. frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 12. frame.setVisible(true); 13. } 14. } 15. 16. /** 17. A frame with a panel for testing various exceptions 18. */ 19. class ExceptTestFrame extends JFrame 20. { 21. public ExceptTestFrame() 22. { 23. setTitle("ExceptTest"); 24. ExceptTestPanel panel = new ExceptTestPanel(); 25. add(panel); 26. pack(); 27. } 28. } 29. 30. /** 31. A panel with radio buttons for running code snippets 32. and studying their exception behavior 33. */ 34. class ExceptTestPanel extends Box 35. { 36. public ExceptTestPanel() 37. { 38. super(BoxLayout.Y_AXIS); 39. group = new ButtonGroup(); 40. 41. // add radio buttons for code snippets 42. 43. addRadioButton("Integer divide by zero", new 44. ActionListener() 45. { 46. public void actionPerformed(ActionEvent event) 47. { 48. a[1] = 1 / (a.length - a.length); 49. } 50. }); 51. 52. addRadioButton("Floating point divide by zero", new 53. ActionListener() 54. { 55. public void actionPerformed(ActionEvent event) 56. { 57. a[1] = a[2] / (a[3] - a[3]); 58. } 59. }); 60. 61. addRadioButton("Array bounds", new 62. ActionListener() 63. { 64. public void actionPerformed(ActionEvent event) 65. { 66. a[1] = a[10]; 67. } 68. }); 69. 70. addRadioButton("Bad cast", new 71. ActionListener() 72. { 73. public void actionPerformed(ActionEvent event) 74. { 75. a = (double[])event.getSource(); 76. } 77. }); 78. 79. addRadioButton("Null pointer", new 80. ActionListener() 81. { 82. public void actionPerformed(ActionEvent event) 83. { 84. event = null; 85. System.out.println(event.getSource()); 86. } 87. }); 88. 89. addRadioButton("sqrt(-1)", new 90. ActionListener() 91. { 92. public void actionPerformed(ActionEvent event) 93. { 94. a[1] = Math.sqrt(-1); 95. } 96. }); 97. 98. addRadioButton("Overflow", new 99. ActionListener() 100. { 101. public void actionPerformed(ActionEvent event) 102. { 103. a[1] = 1000 * 1000 * 1000 * 1000; 104. int n = (int) a[1]; 105. } 106. }); 107. 108. addRadioButton("No such file", new 109. ActionListener() 110. { 111. public void actionPerformed(ActionEvent event) 112. { 113. try 114. { 115. InputStream in = new FileInputStream("woozle.txt"); 116. } 117. catch (IOException e) 118. { 119. textField.setText(e.toString()); 120. } 121. } 122. }); 123. 124. addRadioButton("Throw unknown", new 125. ActionListener() 126. { 127. public void actionPerformed(ActionEvent event) 128. { 129. throw new UnknownError(); 130. } 131. }); 132. 133. // add the text field for exception display 134. textField = new JTextField(30); 135. add(textField); 136. } 137. 138. /** 139. Adds a radio button with a given listener to the 140. panel. Traps any exceptions in the actionPerformed 141. method of the listener. 142. @param s the label of the radio button 143. @param listener the action listener for the radio button 144. */ 145. private void addRadioButton(String s, ActionListener listener) 146. { 147. JRadioButton button = new JRadioButton(s, false) 148. { 149. // the button calls this method to fire an 150. // action event. We override it to trap exceptions 151. protected void fireActionPerformed(ActionEvent event) 152. { 153. try 154. { 155. textField.setText("No exception"); 156. super.fireActionPerformed(event); 157. } 158. catch (Exception e) 159. { 160. textField.setText(e.toString()); 161. } 162. } 163. }; 164. 165. button.addActionListener(listener); 166. add(button); 167. group.add(button); 168. } 169. 170. private ButtonGroup group; 171. private JTextField textField; 172. private double[] a = new double[10]; 173. } |