Thursday, September 15, 2011

JDK7: Java SE 7 Exception Changes

Learn how to take advantage of improved exception handling, one of many useful small language changes found in Project Coin, in Java SE 7.


Introduction

In this article, we cover some changes are a part of the Java Platform, Standard Edition 7 (Java SE 7) release, which are specified in JSR 334, also known as Project Coin. We focus on exception handling, specifically multi-catch, rethrow, and try-with-resources.
Project Coin consists of the following set of small language changes, which are intended to simplify common, day-to-day programming tasks:
  • Strings in switch statements
  • Better integral literals
  • Multi-catch exceptions, which we cover here
  • Improved type inference for generic instance creation ("diamond")
  • try-with-resources, which we cover here
  • Simplified varargs method invocation

Multi-Catch Exceptions

Multi-catch exceptions have been added to Java SE 7 to facilitate easier and more concise exception handling. To migrate your exception handling code from pre-Java SE 7 code to Java SE 7 code read on.
public class ExampleExceptionHandling
{
   public static void main( String[] args )
   {
    try {
     URL url = new URL("http://www.yoursimpledate.server/");
     BufferedReader reader = new 
    BufferedReader(newInputStreamReader(url.openStream()));
     String line = reader.readLine();
     SimpleDateFormat format = new SimpleDateFormat("MM/DD/YY");
     Date date = format.parse(line);
    }
    catch(ParseException exception) {
     // handle passing in the wrong type of URL.
    }
    catch(IOException exception) {
     // handle I/O problems.
    }
    catch(ParseException exception) {
     // handle date parse problems.
    }
   }
}
Example 1
In the past, if you wanted to have the same logic for two of the three cases above, say, the ParseException and the IOException, you had to copy and paste the same code. An inexperienced or lazy programmer might think it would be okay to do the following:
public class ExampleExceptionHandlingLazy
{
   public static void main( String[] args )
   {
    try {
     URL url = new URL("http://www.yoursimpledate.server/");
     BufferedReader reader = new 
    BufferedReader(new InputStreamReader(url.openStream()));
     String line = reader.readLine();
     SimpleDateFormat format = new SimpleDateFormat("MM/DD/YY");
     Date date = format.parse(line);
    }
    catch(Exception exception) {
     // I am an inexperienced or lazy programmer here.
    }
   }
}
Example 2
The biggest problem with the code in Example 2 is that it could introduce unintended side effects. Any code in the try block could throw an exception that would be swallowed up with a blanket (Exceptioncatch clause. If an exception that is not a ParseException orIOException is thrown (for example, a SecurityException), the code would still catch it, but the upstream user would not be aware of what really happened. Swallowing up an exception like that makes problems difficult to debug.
In order to facilitate the programmer’s work, Java SE 7 now includes a multi-catch statement. This allows the programmer to combine acatch clause into a single block of code without the need to use a dangerous catch-all clause or copy entire blocks of code.
public class ExampleExceptionHandlingNew
{
   public static void main( String[] args )
   {
    try {
     URL url = new URL("http://www.yoursimpledate.server/");
     BufferedReader reader = new BufferedReader(
      new InputStreamReader(url.openStream()));
     String line = reader.readLine();
     SimpleDateFormat format = new SimpleDateFormat("MM/DD/YY");
     Date date = format.parse(line);
    }
    catch(ParseException | IOException exception) {
     // handle our problems here.
    }
   }
}
Example 3
Example 3 shows how to properly combine the two statements into one catch block. Notice the syntax for the catch clause (ParseException | IOException). This catch clause will catch both ParseException and IOException.
So, if you now want to share the same exception handling code for two different exceptions you can use the pipe syntax: (ExceptionType| ... | ExceptionType variable).

Rethrowing Exceptions

When doing exception handling, there are times when you want to re-throw an exception that you have handled. An inexperienced programmer might think the following code could do that:
public class ExampleExceptionRethrowInvalid
{
   public static void demoRethrow()throws IOException {
    try {
   // forcing an IOException here as an example,
    // normally some code could trigger this.
  throw new IOException(“Error”);
    }
    catch(Exception exception) {
     /*
      * Do some handling and then rethrow.
      */
     throw exception;
    }
   }
   
   public static void main( String[] args )
   {
    try {
     demoRethrow();
    }
    catch(IOException exception) {
    System.err.println(exception.getMessage());
    }
   }
}
Example 4
But the compiler won't compile the code in Example 4. Example 5 shows a way to handle the exception and then ”rethrow” it:
public class ExampleExceptionRethrowOld
{
   public static demoRethrow() {
    try {
     throw new IOException("Error");
    }
    catch(IOException exception) {
     /*
       * Do some handling and then rethrow.
       */
     throw new RuntimeException(exception);
    }
   }
   
   public static void main( String[] args )
   {
    try {
     demoRethrow();
    }
    catch(RuntimeException exception) {
    System.err.println(exception.getCause().getMessage());
    }
   }
}
Example 5
The problem with the Example 5 is that it is not really rethrowing the original exception. It is wrapping it with another exception, which means that the code downstream needs to be aware that it has been wrapped. So, to make it possible to actually catch the original exception, a change was needed in Java SE (shown in Example 6).
public class ExampleExceptionRethrowSE7
{
   public static demoRethrow() throws IOException {
    try {
     throw new IOException("Error");
    }
    catch(Exception exception) {
     /*
      * Do some handling and then rethrow.
      */
     throw exception;
    }
   }
   
   public static void main( String[] args )
   {
    try {
     demoRethrow();
    }
    catch(IOException exception) {
    System.err.println(exception.getMessage());
    }
   }
}
Example 6

Try-with-Resources

You might have noticed that there is a problem with Example 1 (which is why you should never use example code in a production environment without knowing what it does). The problem is that there is no cleanup of the resources being used inside the try block. Example 7 is an updated version that describes how, prior to Java SE 7, a programmer would address this problem.
public class ExampleTryResources
{
   public static void main(String[] args)
   {
    BufferedReader reader = null;
   
    try {
     URL url = new URL("http://www.yoursimpledate.server/");
     reader = new BufferedReader(new 
    InputStreamReader(url.openStream()));
     String line = reader.readLine();
     SimpleDateFormat format = new SimpleDateFormat("MM/DD/YY");
     Date date = format.parse(line);
    }
    catch (MalformedURLException exception) {
     // handle passing in the wrong type of URL.
    } catch (IOException exception) {
     // handle I/O problems.
    } catch (ParseException exception) {
     // handle date parse problems.
    } finally {
     if (reader != null) {
      try {
       reader.close();
       } catch (IOException ex) {
       ex.printStackTrace();
      }
     }
    }
   }
}
Example 7
Notice that we had to add a final block that closes the BufferedReader if it was ever assigned. Also note that we now have the reader variable outside the try block. This is quite a bit of code if the only thing you want to do is close the reader if an I/O exception occurs.
In Java SE 7, this can be done a lot more concisely and cleanly, as shown in Example 8. The new syntax allows you to declare resources that are part of the try block. What this means is that you define the resources ahead of time and the runtime automatically closes those resources (if they are not already closed) after the execution of the try block.
public static void main(String[] args)
{
   try (BufferedReader reader = new BufferedReader(
    new InputStreamReader(
    new URL("http://www.yoursimpledate.server/").openStream())))
   {
    String line = reader.readLine();
    SimpleDateFormat format = new SimpleDateFormat("MM/DD/YY");
    Date date = format.parse(line);
   } catch (ParseException | IOException exception) {
    // handle I/O problems.
   }
}
Example 8
Note that in Example 8, the actual opening happens in the try ( ... ) statement. Be aware that this feature works only on classes that have implemented the AutoCloseable interface.

Conclusion

The exception handling changes in Java SE 7 allow you not only to program more concisely, as demonstrated in the multi-catch examples, but they also allow you to partially handle an exception and then let it bubble up, as covered in the re-throw examples. Java SE 7 will also facilitate less error-prone exception cleanup, as we saw in the try-with-resources examples. These features, along with other offerings in Project Coin, enable Java developers to be more productive and write more efficient code.

See Also

No comments :

Post a Comment