Going through Herbert Schildt : The Complete Reference Chapter 10 Exception Handling
Exception handling provides a powerful mechanism for controlling complex programs that have many dynamic run-time characteristics. It is important to think of try, throw, and catch as clean ways to handle errors and unusual boundary conditions in your program’s logic. Unlike some other languages in which error return codes are used to indicate failure, Java uses exceptions. Thus, when a method can fail, have it throw an exception. This is a cleaner way to handle failure modes. One last point: Java’s exception-handling statements should not be considered a general mechanism for nonlocal branching. If you do so, it will only confuse your code and make it hard to maintain.
What does he mean by the term 'nonlocal branching'? Kindly elaborate with the help of a nice example?
Note this question was asked on coderanch, but the answers there were primarily based on individual opinions and as such it didn't yield a root-based answer (I wasn't able to clarify the point with the discussions over there).
One of these common bad practices is using exceptions as the control flow. This should be avoided for two reasons: It reduces the performance of your code as a response per unit time, and it makes your code less readable.
If there is no catch block at the current scope matching the thrown exception, the current scope is exited, and all automatic (local nonstatic) objects defined in that scope are destroyed. The surrounding scope (which might be function scope) is checked for a matching handler.
#4) FileNotFoundException: FileNotFoundException is given when the file does not exist or does not open. #5) IOException: IOException is thrown when the input-output operation fails or is interrupted.
Disadvantages. Using exceptions for error handling has two disadvantages. First, exceptions can trap only runtime errors. Therefore, a PL/SQL program cannot trap and recover from compile-time (syntax and semantic) errors such as table or view does not exist.
Here is an example of a local branch:
void foo() {
if (someCondition) {
doSomething();
} else {
doSomethingElse();
}
}
The branching is quite forward to understand, right? The reason for this simplicity is that all branching logic is defined within foo
. Locally, so to speak. This means, a condition is checked and its path is taken depending on someCondition
but the branching does not escape foo
. By only looking at this one method, you know how the code flow might look like.
Now think of this less-readable implementation:
void foo() {
if(bar()) {
// branch is handled by bar
} else {
doSomethingElse();
}
}
boolean bar() {
if(somethingWrong) {
return false;
}
doSomething();
}
This is less readable. But why is that? If you are calling a method bar
from foo
, the control flow is handled by bar
. bar
might however do something unexpected and still rely on foo
to handle this unexpected condition. This is not a good practice as you distribute connected logic among foo
and bar
where a change at one place might cause a misbehavior at another place. This is somewhat what exceptions do if they are treated too far down the stack. You can even extend the complexity of the above example by adding more intermediate methods. Therefore, the book suggests, to keep branching locally as this often is more human readable and traceable what I would equally suggest as a good idea. Look at this exception-based implementation of the above code to visualize this more:
void foo() {
try {
bar()
} catch(SomeException e) {
doSomethingElse();
}
}
boolean bar() {
if(somethingWrong) {
throw new SomeException();
}
doSomething();
}
Obviously, this distributed branch is more error-prone than an if
-else
statement.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With