17. Exception Handling and the Exception Hierarchy in Java
Exception handling is one of the fundamental pillars for creating robust and reliable programs in Java. An exception is an event that occurs during the execution of a program and that interrupts the normal flow of instructions. Java uses an exception handling model that allows developers to manage these abnormal events in a controlled and elegant way.
What are Exceptions?
Exceptions are problems that arise during the execution of a program, such as attempts to access an array index that does not exist, invalid mathematical operations, or attempts to read files that cannot be found. In Java, exceptions are objects that represent these error conditions.
Exception Hierarchy in Java
Exceptions in Java are organized into a class hierarchy. At the top of this hierarchy is the Throwable
class, which is subdivided into two subclasses: Error
and Exception
. The Error
class is used for severe conditions that a normal application should not attempt to catch, such as OutOfMemoryError
. On the other hand, the Exception
class is intended for situations that the application may want to catch and handle.
The Exception
class has two main categories: checked exceptions (checked exceptions
) and unchecked exceptions (unchecked exceptions
). Checked exceptions are those that the compiler requires to be handled or declared in the method that raises them. Unchecked exceptions, which include RuntimeException
and its subclasses, do not need to be declared or handled and generally indicate programming errors such as NullPointerException
or IndexOutOfBoundsException
.
Handling Exceptions
To handle exceptions in Java, we use the try
, catch
, and finally
blocks. The try
block contains code that can raise exceptions, while the catch
block is used to catch and handle these exceptions. The finally
block, which is optional, contains code that runs regardless of whether an exception is thrown or not, usually to free up resources.
try {
// Code that can throw an exception
} catch (ExcecaoType1 e) {
// Treatment for TypeExcecao1
} catch (ExceptionType2 e) {
// Treatment for TypeExcecao2
} finally {
// Code that will be executed after the try or catch
}
It is possible to catch multiple exceptions in a single catch
block starting in Java 7, using the pipe operator (|).
try {
// Code that can throw exceptions
} catch (TypeExcecao1 | TypeExcecao2 e) {
// Handling both exceptions
}
Throwing Exceptions
In addition to handling exceptions, developers can also throw exceptions using the throw
keyword. This is useful to indicate that a method cannot complete its execution normally.
public void fazAlgo() throws MinhaExcecao {
if (someErrorCondition) {
throw new MyExcecao("Error message");
}
}
When a method can throw a checked exception, it must declare this using the throws
keyword in the method signature.
Custom Exceptions
Developers can create their own exceptions by extending the Exception
class or any of its subclasses. This is useful when you want to represent error conditions specific to an application domain.
public class MinhaExcecao extends Exception {
public MinhaExcecao(String message) {
super(message);
}
}
Good Practices in Exception Handling
When handling exceptions, it is important to follow some good practices:
- Catch only exceptions that can be handled in a meaningful way.
- Avoid excessive use of
try-catch
blocks; this can make the code difficult to read and maintain. - Do not catch
Throwable
,Error
orRuntimeException
without a good reason. - Use custom exceptions to make code more understandable and maintainable.
- Document exceptions thrown by methods using the
@throws
tag in Javadoc comments.
In summary, exception handling in Java is a powerful tool that, when used correctly, allows you to create more robust and reliable programs. Understanding the exception hierarchy and knowing how to catch, throw, and create custom exceptions are essential skills for any Java developer.