It is essential to have a firm understanding of Java types and exceptions hierarchy to be able to design your own custom exceptions. We first look at the inheritance structure of Java’s Exception related classes and then turn to types of Exceptions.
Java’s Exception Hierarchy
The following diagram shows the class structure of Java Exception.
The java.lang.Throwable represents any object that can be used to denote an exceptional situation. Throwable extends directly from java.lang.Object and implements the java.io.Serializable interface.
There are two important subclasses of Throwable – java.lang.Error and java.lang.Exception. Technically you can use both of these in your catch blocks, but as we shall see shortly it is almost always pointless to catch java.lang.Error.
The java.lang.Exception class is the class you will be dealing most when you write your own exceptions. java.lang.RuntimeException is a subclass of java.lang.Exception and represents all exceptions that are not checked by the compiler. The RuntimeExceptions are becoming increasingly popular and more and more Java developers are shunning the use of compiler enforced exception checking.
Java’s Exception Types
There are three types of exceptions in Java
Errors are instances of java.lang.Error class. These are abnormal conditions that prevail outside the realm of user’s Java code. Your program (unless it is for launching space shuttle) should never try to catch an Error. You are also not required to declare that an Error may occur during the execution of your code.
Prime examples of errors would be instances of java.lang.VirtualMachineError such as java.lang.OutOfMemoryError, java.lang.StackOverflowError, java.lang.InternalError. Other examples of errors are the ones that can occur during class loading. They include java.lang.NoSuchMethodError, java.lang.NoSuchFieldError etc.
Checked Exceptions are exceptions enforced by the compiler. If you have a statement that throws checked exception, the compiler will force you to either catch it or to re-throw it using the throws statement. We will be covering throw and throws in the next lesson.
A checked exception is an exception that extends from java.lang.Exception but not from java.lang.RuntimeException .
Don’t be under the impression that checked exceptions occur at compile time or cannot occur at runtime. They OCCUR at runtime or class loading time (such as ClassNotFoundException). Just that compiler forces you to deal with the eventuality of such an exception occurring.
Consider for example java.lang.IllegalAccessException. This is a common exception if you are writing reflection code. Reflection is an out and out runtime phenomenon. This exception occurs when you don’t respect the access modifiers.
Be very careful when using your own checked exceptions. They make your code noisy and bulky. You suddenly have a lot of boiler plate code containing throw, throws, try and catch. This bloat further propagates to the developers who use your API. This is the primary reason that checked exceptions have fallen out of favor. We will discuss various approaches to declaring custom exceptions in a later lesson.
Unchecked exceptions are subclasses of java.lang.RuntimeException. These exceptions don’t need to be declared or be caught explicitly. This makes your code concise and elegant. The downside is that these exceptions, if not handled properly, can cause unexpected termination of JVM.
A prime example of unchecked exception is NullPointerException. This exception occurs when you try to access method or field of an object that is not initialized. It typically occurs when you start using object before using the new operator.
Unchecked exceptions are quite often used for programming errors. For example, the NullPointerException described above. It is caused by invoking methods on null objects. Another example would be ArrayIndexOutOfBoundsException which occurs when you try to access an array beyond its length.