Exceptions are a more object-oriented approach to handling exceptional execution flows than return codes. The drawback of return codes is that you have to come up with 'special' values to indicate different types of exceptional results, for example:
public double calculatePercentage(int a, int b) {
if (b == 0) {
return -1;
}
else {
return 100.0 * (a / b);
}
}
The above method uses a return code of -1 to indicate failure (because it cannot divide by zero). This would work, but your calling code needs to know about this convention, for example this could happen:
public double addPercentages(int a, int b, int c, int d) {
double percentage1 = calculatePercentage(a, b);
double percentage2 = calculatePercentage(c, c);
return percentage1 + percentage2;
}
Above code looks fine at first glance. But when b or d are zero the result will be unexpected. calculatePercentage will return -1 and add it to the other percentage which is likely not correct. The programmer who wrote addPercentages is unaware that there is a bug in this code until he tests it, and even then only if he really checks the validity of the results.
With exceptions you could do this:
public double calculatePercentage(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("Second argument cannot be zero");
}
else {
return 100.0 * (a / b);
}
}
Code calling this method will compile without exception handling, but it will stop when run with incorrect values. This is often the preferred way since it leaves it up to the programmer if and where to handle exceptions.
If you want to force the programmer to handle this exception you should use a checked exception, for example:
public double calculatePercentage(int a, int b) throws MyCheckedCalculationException {
if (b == 0) {
throw new MyCheckedCalculationException("Second argument cannot be zero");
}
else {
return 100.0 * (a / b);
}
}
Notice that calculatePercentage has to declare the exception in its method signature. Checked exceptions have to be declared like that, and the calling code either has to catch them or declare them in their own method signature.
I think many Java developers currently agree that checked exceptions are bit invasive so most API's lately gravitate towards the use of unchecked exceptions.
The checked exception above could be defined like this:
public class MyCheckedCalculationException extends Exception {
public MyCalculationException(String message) {
super(message);
}
}
Creating a custom exception type like that makes sense if you are developing a component with multiple classes and methods which are used by several other components and you want to make your API (including exception handling) very clear.
(see the Throwable class hierarchy)