Efficiency aside, the second way is preferred, because the exception is not thrown in a non-exceptional situation. The first way to "dispatch" uses exception throwing in a regular control flow, which makes it harder to read your program.
Besides, the two methods are not identical: the first program must be declared as throwing a checked exception, because not all subtypes are handled by the catch
blocks.
If you are dealing with custom exceptions that your program defines, you have a way to avoid checking the subtype: since Exception
objects are regular classes, you could add a package-visible method to them, and have them implement a package-visible interface holding that method. Exceptions would then be able to override that method, letting you use a regular "virtual" dispatch rather than checking for the exact class type at runtime.
Here is an illustration of this approach: let's say you want your exceptions to write themselves to a log file. You can do this as follows:
interface LoggableException {
void logToFile();
}
public class CustomExceptionOne extends Exception implements LoggableException {
...
public void logToFile() {
// Do something
}
}
public class CustomExceptionTwo extends Exception implements LoggableException {
...
public void logToFile() {
// Do something else
}
}
public void processException(Exception ex) {
if (ex instanceof LoggableException) {
LoggableException lEx = (LoggableException)ex;
lEx.logToFile();
}
}