Tuesday, September 23, 2014

Java Exception Handling

Exception Handling
According to The Java Tutorials: "An exception is an event, which occurs during the execution of a program, that disrupts the normal flow of the program's instructions."
Checked vs Unchecked Exceptions
There are two types of exceptions in Java.
There are checked exceptions and there are also unchecked exceptions.

Checked exceptions get their name because the compiler checks that they are explicitly handled either in a try-catch block…
try {
   // …
}
catch(IOException io) {
   // …
}
…or in the throws clause of a method’s declaration.
public void fooBar( ) throws IOException {
There are certain types of exceptions, like the FileNotFoundException, that typically occur because of user error and should therefore be anticipated by your program. That's why Java checks that you've dealt with them somehow.

Unchecked exceptions extend from either the Error class or the RuntimeException. They're called unchecked because the compiler doesn't check to see if they are caught or thrown. The NullPointerException and the IllegalArgumentException are two examples of unchecked exceptions.

Error indicates a serious problem that typically occurs under abnormal conditions. Since recovering from an Error is generally not possible, most programs simply just terminate. OutOfMemoryError and StackOverflowError are two example subclasses of Error.
throw vs throws
You can explicitly cause an exception to occur using the throw keyword. For lack of a better example, suppose you had the following setAge method:
public void setAge(int age) {
Obviously there's a range of acceptable values that a person's age can be. For example it can't be negative. You can ensure that the client conforms to these restrictions by using an if statement like so:
if(age < 0) {
   throw new IllegalArgumentException("A person's age can't be negative; age = " + age);
}
Since IllegalArgumentException is a subclass of RuntimeException you don't need to explicitly state that the setMethod throws it. You should probably document somewhere that this exception might occur if you try to pass in a value outside the acceptable range.
/**
* @throws IllegalArgumentException if age < 0
*/
public void setAge(int age) {
Only checked exceptions must be listed in the throws clause of a method's declaration.
For example if you were writing the value to a file an IOException might occur.
public void setAge(int age) throws IOException {
   // ....
You can only throw objects that either directly or indirectly extend from the java.lang.Throwable class. The Throwable class is the superclass of all errors and exceptions in the Java language.

Throwable's toString() method returns a concatenation of three items:
  • the class name of the exception
  • a colon and a space, ie ": "
  • the result from getMessage(), unless it is null in which case an empty string is returned

For example, given the following...
public static void main(String[] args) {
   try {
      throw new NullPointerException("some message");
   }
   catch(Exception exception) {
      System.out.println("class name: " + exception.getClass().getName());
      System.out.println("getMessage() -> " + exception.getMessage());
      System.out.println("toString() -> " +  exception.toString());
   }
}
...then this is the result.
class name: java.lang.NullPointerException
getMessage() -> some message
toString() -> java.lang.NullPointerException: some message
Overriding Methods That Throw Exceptions
You have several options to choose from when overriding a method that throws an exception:

You can just rethrow the exception:
import java.io.IOException;

public interface MyInterface {
   public void fooBar() throws IOException;
}
import java.io.IOException;

public class Klass implements MyInterface {

   @Override
   public void fooBar() throws IOException {
   }
}
You can throw a subclass of the exception:
import java.io.FileNotFoundException;

public class Klass implements MyInterface {

   @Override
   public void fooBar() throws FileNotFoundException {
   }
}
You can even choose not to throw the exception:
import java.io.FileNotFoundException;

public class Klass implements MyInterface {

   @Override
   public void fooBar() {
   }
}
The one thing you cannot do, however, is add additional checked exceptions to the throws statement:
public class Klass implements MyInterface {

   @Override
   public void fooBar() throws CloneNotSupportedException { // Compiler Error: 
      // Overriden method does not throw java.lang.CloneNotSupportedException
   }
}
Catching Unthrown Checked Exceptions
The Java compiler will complain if you try to catch a checked exception that never gets thrown.
try {
}
catch(IOException io) {
}
The error message you'll get will look something like the following:
"Exception java.io.IOException is never thrown in body of corresponding try block"
Catching Multiple Exceptions
If you need to catch multiple exceptions you'll have to specify the most specific exceptions first and then the more generic ones later. For instance, the Java compiler will complain if you try to give it the following.
try {
}
catch(IllegalArgumentException illegalArgument) {
}
catch(NumberFormatException numberFormat) {
}
The error message you'll get will look similar to this:
Exception 'java.lang.NumberFormatException' has already been caught
Here's the correct way to do this:
try {
}
catch(NumberFormatException numberFormat) {
}
catch(IllegalArgumentException illegalArgument) {
}
Java 7 introduced a new syntax that allows you to catch multiple exceptions in one catch block. The catch (no pun intended) is that you cannot use a subclass of one of the exceptions. For instance, given the following...
try {
}
catch(NumberFormatException | IllegalArgumentException exception) {
}
...the Java compiler will issue an error message that will look similar to this:
Types in multi-catch must be disjoint:
  'java.lang.NumberFormatException' is a subclass of
  'java.lang.IllegalArgumentException'
When a catch block handles more than one exception, the parameter used in the catch block is implicitly made final. What that means is you cannot change the parameter variable to point to a different Exception object.
try {
   throw new NullPointerException();
}
catch(NullPointerException nullPointer) {
 nullPointer = new NullPointerException();
}

try {
   throw new NullPointerException();
}
catch(NullPointerException | IllegalArgumentException exception) {
   exception = new Exception(); // Compiler Error
}
The finally Block
The finally block is always executed after the try block, even if an exception is thrown.

Consider the following example.
Even though the catch block has a return statement, the finally block's return statement takes precedence and the method returns 3.
public static int test() {
  try {
    System.out.println("inside try");
         
    String str = null;
    str.length();

    return 1;
  }
  catch (NullPointerException nullPointer) {
    System.out.println("inside catch");

    return 2;
  }
  finally {
    System.out.println("inside finally");

    return 3;
  }
}   
The finally block is optional if there is at least one catch block.
try-with resources
Suppose you wanted to read data from a text file. The following is what your code might look like:
try {
   String line = null;
   BufferedReader bufferedReader = new BufferedReader(new FileReader(file));

   while ((line = reader.readLine()) != null) {
      // process line that was read...
   }
}
catch(FileNotFoundException fileNotFound) {
   // handle FileNotFoundException
}
catch(IOException io) {
   // handle IOException
}
finally {
   if(bufferedReader != null) {
      try {
         bufferedReader.close();
      }
      catch(IOException io) {
      }
   }
}
That's quite a bit of code, but for now we're only interested in the close method.

Prior to Java 5 there really was no standard for how a resource should be closed. Java 5 fixed this with the java.io.Closeable interface. Notice how ugly closing the resource is, though. First you have to check that the resource was actually instantiated. Then you have to wrap the close method in a try-catch block.

This is where Java 7's java.lang.AutoCloseable interface comes into play. It ensures that the resource is closed either when the end of the try block has been reached or an exception has occurred. Any class that implements the AutoCloseable interface may be instantiated inside a try statement, which Java calls a try-with-resource statement.

Here's what the above code fragment looks like in Java 7:
try(BufferedReader bufferedReader = new BufferedReader(new FileReader(file))) {
   String line = null;
   
   while ((line = reader.readLine()) != null) {
      // process line that was read...
   }
}
catch(FileNotFoundException fileNotFound) {
   // handle FileNotFoundException
}
catch(IOException io) {
   // handle IOException
}
Another interesting aspect of the try-with-resource statement is that you're not required to implement a finally block if there are no catch blocks.
public void fooBar() throws FileNotFoundException, IOException {

   try(BufferedReader bufferedReader = 
      new BufferedReader(new FileReader("stuff.txt"))) {

      String line = null;

      while ((line = bufferedReader.readLine()) != null) {
         // process line that was read...
      }
   }
}
Recall that the finally block is optional if there is at least one catch block.
import java.io.IOException;

public class Main {

   public static void main(String[] args) throws IOException {
      try {
         new IOException();
      } // Compiler Error: catch or finally expected
   }
}
You can even instantiate multiple resources inside the try-with-resources statement. Just separate them with a semicolon ; .

Consider the following:
public class Foo implements AutoCloseable {

   @Override
   public void close() throws Exception {
      System.out.println("called Foo's close method");
   }
}
public class Bar implements AutoCloseable {

   @Override
   public void close() throws Exception {
      System.out.println("called Bar's close method");
   }
}
public static void main(String[] args) throws Exception {
   try(Foo foo = new Foo(); Bar bar = new Bar()) {
      System.out.println("inside try");
   }
}
The close method of each resource is invoked in reverse order of its instantiation.

Therefore the result of the above program would be the following:
inside try
called Bar's close method
called Foo's close method
The java.io.Closeable interface extends from java.lang.AutoCloseable.
public interface Closeable extends AutoCloseable
According to the Java documentation, Closeable's close method is required to be idempotent. That is, you can call it multiple times with no side visible side effects. AutoCloseable's close method, on the other hand, does not have this requirement although it is strongly encouraged.

Another difference between the close method provided by these two interfaces is that AutoCloseable's throws an Exception while Closeable's throws an IOException.

There's still one last topic to cover with the try-with-resource statement.

If you'll recall, the resource is closed either when the end of the try block has been reached or an exception has occurred.

Suppose the close method also throws an Exception.
public class Foo implements AutoCloseable {

   @Override
   public void close() throws Exception {
      throw new Exception("Foo's close() method");
   }
}
public static void main(String[] args) {
   try(Foo foo = new Foo()) {
      throw new Exception("inside try block");
   }
   catch (Exception exception) {
      System.out.println(exception.toString());
   }
}
So which Exception do you think gets thrown?

Output
java.lang.Exception: inside try block
As you can see from this example, the Exception that occurred inside the try-block is the one that gets thrown. But what about the Exception that occurred inside the close method. How are we supposed to fix that exception if it's being “swallowed” by the one inside the try-block? Fortunately Throwable has a getSuppressed( ) method which returns an array of Throwable objects (ie. Exceptions) that were “swallowed” inside the try-block.
public static void main(String[] args) {
   try(Foo foo = new Foo()) {
      throw new Exception("inside try block");
   }
   catch (Exception exception) {
      for (Throwable throwable : exception.getSuppressed()) {
         System.out.println("(suppressed) " + throwable.toString());
      }

      System.out.println(exception.toString());
   }
}
Output
(suppressed) java.lang.Exception: inside Foo's close() method
java.lang.Exception: inside try block
References

No comments:

Post a Comment