In this section, we examine the cost of exceptions and consider ways to avoid that cost. First, we look at the costs associated with try-catch blocks, which are the structures you need to handle exceptions. Then, we go on to optimizing the use of exceptions.
Running this test in various VMs results in increases in the time taken by the looped try-catch test relative to the nonlooped test for some VMs; however, the latest VMs show no penalty. See Table 6-1.
Throwing an exception and executing the catch block has a significant overhead. This overhead seems to be due mainly to the cost of getting a snapshot of the stack when the exception is created (the snapshot allows the stack trace to be printed). The cost is large: exceptions should not be thrown as part of the normal code path of your application unless you have factored it in. Generating exceptions is one place where good design and performance go hand in hand. You should throw an exception only when the condition is truly exceptional. For example, an end-of-file condition is not an exceptional condition (all files end) unless the end-of-file occurs when more bytes are expected. Generally, the performance cost of throwing an exception is equivalent to several hundred lines of simple code executions.
 There are exceptions to the rule. For example, in Section 7.3 in Chapter 7, the cost of one exception thrown is less than the cost of repeatedly making a test in the loop, though this is seen only if the number of loop iterations is large enough.
If your application is implemented to throw an exception during the normal flow of the program, you must not avoid the exception during performance tests. Any time costs coming from throwing exceptions must be included in performance testing, or the test results will be skewed from the actual performance of the application after deployment.
The results of this comparison show that if test2( ) (using instanceof) takes one time unit, test1( ) with the ClassCastException thrown takes over 100 time units in JDK 1.4 (see the first line in Table 6-2). The second line in Table 6-2 shows the relative cost of throwing the exception with different parameters passed to test1( ), and also shows that throwing the exception is very costly. The two lines together show that using instanceof is fairly efficient.
 The 1.4 JVM JIT compiler in server mode identified that the test was effectively a repeated constant expression and collapsed the loop to one call, thus eliminating the test. The costs of using exceptions are still present in 1.4.0 server mode, but this test cannot show those costs.
For VMs not running a JIT, or using HotSpot technology, the relative times for test2( ) are different depending on the object passed. test2( ) takes one time unit when returning true but, curiously, two to ten time units when returning false. This difference for a false result indicates that the instanceof operator is faster when the instance's class correctly matches the tested class. A negative instanceof test must also check whether the instance is from a subclass or interface of the tested type before it can definitely return false. Given this, it is actually quite interesting that with a simple JIT, there is no difference in times between the two instanceof tests.
Because it is impossible to add methods to classes that are compiled (as opposed to classes you have the source for and can recompile), there are necessarily places in Java code where you have to test for the type of object. Where this type of code is unavoidable, you should use instanceof, as shown in test2( ), rather than a speculative class cast. There is no maintenance disadvantage in using instanceof, nor is the code any clearer or easier to alter by avoiding its use. I strongly advise you to avoid the use of the speculative class cast, however. It is a real performance hog and ugly as well.
The sole disadvantage of reusing an exception instance is that the instance does not have the correct stack trace, i.e., the stack trace held by the exception object is the one generated when the exception object was created. This disadvantage can be important for some situations when the trace is important, so be careful. This technique can easily lead to maintenance problems.
 To get the exception object to hold the stack trace that is current when it is thrown, rather than created, you must use the fillInStackTrace( ) method. Of course, this is what causes the large overhead that you are trying to avoid.
This technique allows you to turn off error checking by recompiling the application with the ERROR_CHECKING_ON variable set to false. Doing this recompilation actually eliminates all if blocks completely, due to a feature of the compiler (see Section 3.9.1.4 in Chapter 3). Setting the value to false without recompilation also works, but avoids only the block, not the block entry test. In this case, the if statement is still executed, but the block is not entered. This still has some performance impact: an extra test for almost every method call is significant, so it is better to recompile.
 However, this technique cannot eliminate all types of code blocks. For example, you cannot use this technique to eliminate try-catch blocks from the code they surround. You can achieve that level of control only by using a preprocessor. My thanks to Ethan Henry for pointing this out.