Java Notes
Exceptions - More
Kinds of Exceptions
There are many exceptions, but they can be put into two groups: checked exceptions and unchecked exceptions. There is some controversy about which type you should use. A discussion of some of the issues can be found at Java theory and practice: The exceptions debate.
- Unchecked Exceptions -- These exceptions are usually something
that should have been prevented by more careful programming.
For example, you should never get
NullPointerException
orArrayIndexOutOfBoundsException
. If you do, there is something wrong with your program, and you need to fix it. You usually don't catch unchecked exceptions. Instead, fix your program so it can't produce one of these. However,NumberFormatException
is the one exception of this type that is usually caught. - Checked Exceptions -- These are usually errors in the input data.
The programmer has no control over the input the user gives you,
eg, file names, .... If the user gives you a bad
value, it may cause an exception when you use it.
You need to check for bad input using a
try
statement.
Use exceptions for exceptional conditions, NOT normal control flow
Probably most of your student programming has been "Happy Trails" style, where you didn't have to worry much about handling errors. But error handling is really a big deal in most real programs, and exceptions play a central role in dealing with errors.
All experienced programmers agree that using exceptions for normal processing flow is wrong. Exceptions should only be used only for errors or unusual conditions, and the equivalent if tests should be used for normal processing. There are good reasons for this.
- Slow. Exceptions are very slow. When an exception occurs, the Java runtime system works its way up the call stack (you might be surprised at how deep this can get), identifying each source statement at which a call was made, and building a string that reports this. This string is then used in creating the exception object that is thrown. This is not fast.
- Readability. Experienced programmers expect exceptions to have something to do with errors, so it is very uncomfortable to read code where it is used for normal flow. A programmer has to stop and examine the code in the try clause to see what might have caused the exception.
Here are examples where beginning programmers used exceptions, but should NOT have. These are all done to either speed execution (which they do not) or simplify code (which they arguably do not).
Example: Test for end of array index range
Good | BAD |
---|---|
int[] a = new int[1000]; for (int i=0; i < a.length; i++) { a[i] = i; } You might wonder how inefficient this is because the loop must compare the index with the array size 1000 times, but only the final test is important. Because Java always checks the subscript range anyway, why not make use of its check? |
int[] a = new int[1000]; try { for (int i=0; ; i++) { // No range check. a[i] = i; } } catch (ArrayIndexOutOfBoundsException e) { } Exceptions are so slow that this won't be faster unless the array is extremely large (much, much larger than 1000). |
Avoiding edges in looking for adjacent array cells.
Problem: You must invert the values in a cell in a rectangular grid and its non-diagonally adjacent cells. The difficulty is in dealing with the edge cases, where you must avoid referencing non-existent adjacent cells.
Two alternate definitions of a method are given, one uses exceptions and the other uses if to handle the boundary violation cases. The exception solution is very inefficient and might be very hard to interpret by the reader. The difficulty is increased because the writer chose to use the Exception class instead of ArrayIndexOutOfBoundsException. The use of Exception suggests that it is designed to catch other exceptions too. if the body of the try had been larger, it might have been very difficult decide exactly which exception is being caught. Do you see which other exception could be thrown by the code, at least in principle?
private boolean[][] cell = new boolean[SIZE][SIZE]; . . . // BAD public void flipA(int row, int col) { cell[col ][row ] = !cell[col][row]; try { cell[col+1][row ] = !cell[col+1][row ];} catch(Exception e) {} try { cell[col-1][row ] = !cell[col-1][row ];} catch(Exception e) {} try { cell[col ][row+1] = !cell[col ][row+1];} catch(Exception e) {} try { cell[col ][row-1] = !cell[col ][row-1];} catch(Exception e) {} } // Much better (faster and less worrisome to the normal reader) public void flipB(int row, int col) { cell[col ][row ] = !cell[col ][row ]; if (col < SIZE-1) cell[col+1][row ] = !cell[col+1][row ]; if (col > 0 ) cell[col-1][row ] = !cell[col-1][row ]; if (row < SIZE-1) cell[col ][row+1] = !cell[col ][row+1]; if (row > 0 ) cell[col ][row-1] = !cell[col ][row-1]; }
Another solution to avoid edge cases is to define extra rows and columns of boundary cells, and translate the subscripts, thereby replacing the if tests with two additions. This requires translating subscript references in all methods. If the class is properly encapsulated, users of the class will not know about it.
private boolean[][] cell = new boolean[SIZE+2][SIZE+2]; . . . public void flipC(int r, int c) { int row = r + 1; int col = c + 1; cell[col ][row ] = !cell[col ][row ]; cell[col+1][row ] = !cell[col+1][row ]; cell[col-1][row ] = !cell[col-1][row ]; cell[col ][row+1] = !cell[col ][row+1]; cell[col ][row-1] = !cell[col ][row-1]; }
Other examples
There are numerous cases in addition to subscription where the use of exceptions is entirely inappropriate.
Danger from the intermediate layers - finally
Exceptions provide a good infrastructure for error processing.
The simplicity of throwing an exception at a deep level and catching
it at a high level may generate problems at the intermediate, skipped,
levels. Did any of these methods leave any part of a data structure
or resource in an inconsistent state?
Each place that this may be true of needs to enclose critical code
in a try...finally
block.
References
- Exception-Handling Antipatterns by Tim McCune, today.java.net/pub/a/today/2006/04/06/exception-handling-antipatterns.htm, is a good article if you're ready for some advanced exception usage.
- Three Rules for Effective Exception Handling by Jim Cushing, http://today.java.net/pub/a/today/2003/12/04/exceptions.html, has some useful "rules", but even more useful is the list of links what was posted in response to this article.