Table of Contents
Exceptions allow methods to report to the calling code about any issues, errors or anomalous situations generated during its execution.
For example, let's suppose a method that receives a postal code and returns the name of the town that it belongs to. In the normal case that the postal code is correct, the method would just return the name of the town. In the case that the received postal code does not exist or has an incorrect format, the method would notify it throwing an exception. An exception in Java is an object of the Exception
class or any of its subclasses.
Java provides many predefined exception classes for certain situations, such as for example IOException
for errors related to input/output operations, such as reading files.
For our own programs, sometimes it is useful to create our own ad-hoc
exceptions. To do so, we have to declare a new class that inherits from
Exception
. For example:
public class BadPostCodeException extends Exception { public BadPostCodeException() { super(); } public BadPostCodeException(String message) { super(message); } }
We do not usually need to declare new attributes or methods, apart from the constructors.
When inside a method we need to throw and exception, we will use the
throw
keyword, followed by an instance of the exception class
to throw. For example:
if (postCode.length() != 5) { throw new BadPostCodeException("Postcodes must have 5 digits"); } for (int i = 0; i < postCode.length(); i++) { if (!Character.isDigit(postCode.charAt(i))) { throw new BadPostCodeException("Postcodes can only contain digits"); } } City city = lookupPostCode(postCode); if (city == null) { throw new BadPostCodeException("Postcode not in database: " + postCode); }
Except for a special subset of exceptions, in general a method that can (potentially) throw an exception must declare it explicitely, using the throws
keyword (pay attention to the final "s", and don't confuse it with throw
) followed by the name of the exception class that it may throw:
public class PostCodeManager { (...) public String town(String postCode) throws BadPostCodeException { if (postCode.length() != 5) { throw new BadPostCodeException("Postcodes must have 5 digits"); } for (int i = 0; i < postCode.length(); i++) { if (!Character.isDigit(postCode.charAt(i))) { throw new BadPostCodeException("Postcodes can only contain digits"); } } City city = lookupPostCode(postCode); if (city == null) { throw new BadPostCodeException("Postcode not in database: " + postCode); } (...) } (...) }
If the method can throw exceptions of different classes, all the exception
class names must be explicitely stated in the declaration, separated by
commas, following one only throws
.
When a exception happens, the virtual machine interrupts the normal execution flow of the program, and searches for a block of code appropriate for dealing with the situation. If such piece of code is not found in the current method, the exception propagates (is thrown) to the invoking method, and searches for the code to deal with the exception there. If that method don't provide the appropriate code either, the exception will propagate again to the following invoking method, and so on.
This section explains the case when a method does not provide the code appropriate for dealing with an exception. As explained before, in this case the exception propagates (is thrown) to the calling method. A method that does not provides code for dealing with the exception must declare that it may throw it with the throws
keyword, as explained in the previous section:
public class Location { (...) private String postCode; public String townAsString() throws BadPostCodeException { PostCodeManager manager = new PostCodeManager(); String town = manager.town(postCode); return postCode + " " + town; } (...) }
In the example, the call to the town
method may potentially throw an exception. The
townAsString
method does not contain code for treating it. In consequence, the exception would propagate through it (it would be thrown again to the method calling townAsString), thus must declare that it may throw it, as shown in the example.
If during the call to town
an exception happens, such exception will be propagated to the method that has called to
townAsString
.
The exception will continue propagating through the call stack, until it is captured (treated) in a method. If the
main
method is reached, and it does not provide any code for dealing with it either, the virtual machine will interrupt the execution flow of the program and show the user, usually on the console, the message of the exception and the place in the program where it happened.
A method can decide to capture an exception if it is logic to put in such method the code that process it appropriately. For capturing an exception, a
try
/catch
block is used:
try {
// code where the exception may happen
} catch (ExceptionClass e) {
// code that process the exception situation
}
When the exception happens in the
try
block, the execution is interrupted and the code in the catch
block starts executing. Once finalised the
catch
block, the execution continues with the code following the try
/catch
block. Usually, it continues also at that point when the
try
code ends without exceptions.
An alternative implementation of
townAsString
in which the exception is captured is shown below:
public class Location { (...) private String postCode; public String townAsString() { String result; PostCodeManager manager = new PostCodeManager(); try { String town = manager.town(postCode); result = postCode + " " + town; } catch (BadPostCodeException e) { result = "Unknown location at postcode " + postCode; } return result; } (...) }
If an exception is generated inside the town
method, the
execution of the code in the try
block is interrupted, and
thus the result = postCode + " " + town
instruction is no
longer executed, but the program jumps to the catch
block and
executes the code inside it. The return result
instruction is
always executed, independently of having or not an exception, because it
is outside and following the try
/catch
block.
Pay attention that the townAsString
does not declare in this
case that it throws the exception. As it captures the exception, it no
longer propagates through it.