Bits of Java – Episode 20: Java Exceptions
This week we will discuss a bit what happens when there is something wrong with your Java application. There are two main groups of things that could go wrong in your code, and we can distinguish them based on, let’s say, how early Java can detect the problem.
In this sense, we have compiler errors, that, as the name suggests, are detected at compiler time, so before the application actually runs. If you are programming within an IDE, usually you get a clear error at the exact line which is causing the compiler to complain, and you probably also get a suggestion about what’s going on and how to fix it.
And then we have runtime errors. These are errors that cannot be detected by the compiler, but that could arise once your application is running. Java lets you know about these kind of errors by throwing exceptions.
The parent of all the exceptions is the Throwable
class in Java, meaning that all Java built-in exceptions extend such class. Among them we have three important classes: Exception
, RuntimeException
and Error
. All the other exceptions then have one, or more of them, as parent. I say “or more” because RuntimeException
actually extends Exception
, so if a class has as direct parent the first one, it would also indirectly inherit the second.
Based on which parent class they have, we can distinguish three different types of exceptions:
- checked exceptions are the ones that inherit from
Exception
but not fromRuntimeException
; - unchecked exceptions are those which inherit from
RuntimeException
; - errors are the ones which inherit from
Error
.
This distinction is not just a pure nomenclature matter, but is something one has to keep in mind when working with exceptions, both the built-in and his own declared ones. The reason is that, depending on the kind of exception, the way we have (or have not) to handle them changes.
Let’s see what I mean by that. There could be a lot of reasons why an application throws an exception, right? A file not found, an attempt to access a null reference, a division by zero, a memory issue, and so on. Some of them are clearly related to a particular operation that you could do in your code. For instance, you will never get a problem with a file not found if you do not try to look for any file in your application, right? Some other exceptions, on the contrary, can potentially happen at any point through the execution of your application, without you being able to anticipate it.
In this sense, you can think about the checked exceptions as the kind of exception that can potentially happen due to a particular action you have coded, and, so, you should be fully aware of the possibility of an exception and you should take care of it, or at least warn the others about it.
Unchecked exceptions are, instead, exceptions that you could not really foresee, so you are not really obliged to take care of them, but, if you see the possibility of such an exception you can in principle do something about that.
Lastly, errors are indications that something has gone really bad during execution, and you should never try to recover from such a failure.
So, we have given just a description on the different kind of exceptions you can encounter in Java, but we still do not know what exactly means to throw an exception, or to take care of an exception, or even warn the others about an exception.
Let’s start from this last one. To warn that a method could potentially throw an exception you have to specify it in the method declaration.
public void readFile(String fileName) throws IOException {
//do something that can potentially
//result in an IOException
}
The keyword is throws
. By declaring the exception, you are just saying to whoever wants to call this method, that it could throw an exception of that kind. You are not actually doing anything to recover from the exception itself, but you are passing this problem to the callers of the method, which, in turns, would have to handle the exception by themselves or redeclare it and make it someone else’s problem. Checked exceptions which are not handled have to be declared.
The keyword throws
is not to be confused with throw
, without a final s
, which is instead used when you want to throw an exception.
public void readFile(String fileName) throws IOException {
throw new IOException();
}
You cannot throw a checked exception if you have not declared it first.
Handle an exception, instead, means that you take some action in case the exception would be thrown, usually to recover to such an exception and not interrupt the execution of the application. In Java exceptions are handled by try-catch
blocks.
public void readFile(String fileName) {
try {
//do something that could
//result to IOException
} catch(IOException e) {
//if the IOException is
//thrown enter here to recover!
}
}
Checked exceptions which are not declared have to be handled. The catch
block is only executed if the code within the try
actually rises an exception of a compatible type with the one in the catch
. You can have zero or more catch
blocks, but, if there is none, you have to insert a finally
block. Otherwise, the finally
block is optional. This is always executed, no matter whether the exception is actually thrown or not.
public void readFile(String fileName) {
try {
//do something that could
//result to IOException
} catch(IOException e) {
//if the IOException is
//thrown enter here to recover!
} finally {
//do something in any case
}
}
And that’s it! Now you know how to declare and handle exceptions in Java. Next week we will talk about lambdas expressions and functional interfaces!
by Ilenia Salvadori