The idea behind exception handling is that you can handle errors at points in your program flow where you can deal with them meaningfully. Instead of checking every return value of the function, like in C, where most of the time you can’t do anything reasonable, except passing the error on, you install the try / catch block at reasonable points in your program:
Basically, whenever there is a point at which you can react to an error, then catch that error and pass on everything else. Thus, error handling is called only when there is a plausible recovery from the error.
For example, in the worst case scenario, if any error stops your program from meaningful execution, then you will catch almost nothing and just let the OS handle the situation (well, maybe one try / catch attempt to create a friendly error message).
Example (in C ++, sorry, I cannot type Java blind):
int main() { try { while (masterloop()) { } catch (...) { LOG("Fatal program error, terminating!"); // nothing else we can do! } } /* lots of program logic */ void process_image() { try { Image im = load_image_from_disk(); /* ... */ } catch (const OutOfMemoryExc & e) { LOG("Not enough memory to process the image."); return; } catch (const DataErrorExc & e) { LOG("Could not read the image data."); return; } catch (...) { throw; // pass everything else along } }
In this example, we can try to process the image and fail for some reason (memory or inability to read the image). In this case, we simply return without doing work, and let the program continue gracefully. All other errors propagate to the highest point. Most importantly, we do not have to constantly put the current image processing function with error checks and answers, enough for any code to throw one of our two good exceptions and no longer worry.
Moral: If you have try / catch blocks absolutely everywhere, you are doing it wrong.