Java7 – A look back
I started writing a blog post on what’s new in the upcoming Java8 release, and thought I would start by doing a quick look back at what Java7 brought to us.
Java7 was released back in July 2011, and was described as “more evolutionary than revolutionary”.
“There are some significant improvements, but no really earth-shattering or ground-breaking kinds of features.” – Oracle chief Java architect Mark Reinhold
It didn’t contain the much hyped lambda expressions for example. Still, it did bring a lot of other neat additions to the Java language. You can see the more at http://docs.oracle.com/javase/7/docs/, or read on for my summary.
The highlights of Java7 included:
- Project coin
- Strings in switch
- try-with-resources statement
- Multi-catch and more precise rethrow
- The diamond operator
- Binary integer literals
- Underscores in numeric literals
- New Input/Output features
- Fork & Join / Concurrency utilities
Project Coin
Project coin, a pun on ‘small change’, contain a few minor but useful features, including:
Strings in switch
Pre Java 7, switch statements worked either with primitive types or enumerated types. Java 7 introduced another type that we can use in Switch statements: the String
type. Say we have a requirement to process a Trade based on a String status field. Until now we used to do this by using if-else statements.
private void processTrade_UsingIfs(Trade t) { String status = t.getStatus(); if (status.equalsIgnoreCase("New")) { setupNewTrade(t); } else if (status.equalsIgnoreCase("Execute")) { executeTrade(t); } else if (status.equalsIgnoreCase("Pending")) { processTrade(t); } }
In Java7, we can do the same thing using a switch statement:
public void processTrade_UsingSwitch(Trade t) { String status = t.getStatus(); switch (status) { case "New": setupNewTrade(t); break; case "Execute": executeTrade(t); break; case "Pending": processTrade(t); break; default: break; } }
Automatic resource management in try-statement
You can now declare a resource in the try block that will be automatically closed. e.g. used to have to do:
public void oldTry() { FileReader fileReader = null; BufferedReader inputStream = null; try { fileReader = new FileReader("java7.txt"); inputStream = new BufferedReader(fileReader); String line = inputStream.readLine(); System.out.println(line); } catch (IOException e) { //typically log or rethrow } finally { //all resources need to be manually closed try { fileReader.close(); inputStream.close(); } catch (IOException e) { //typically ignore } } }
Now in Java 7 however, the try-with-resources statement ensures that each resource is closed at the end of the statement.
public void newTry() { try ( FileReader fileReader = new FileReader("java7.txt"); BufferedReader inputStream = new BufferedReader(fileReader) ) { String line = inputStream.readLine(); System.out.println(line); } catch (IOException e) { //typically log or rethrow } //no finally block to close resources required }
The declaration of the resources appears within parentheses immediately after the try keyword.
Any object that implements java.lang.AutoCloseable, which includes all objects which implement java.io.Closeable, can be used as a resource. Resources will be closed regardless of whether the try statement completes normally or abruptly
Multi-catch and more precise rethrow
Multi catch
public void oldMultiCatch() { try { methodThatThrowsThreeExceptions(); } catch (ExceptionOne e) { // log and deal with ExceptionOne } catch (ExceptionTwo e) { // log and deal with ExceptionTwo } catch (ExceptionThree e) { // log and deal with ExceptionThree } } public void newMultiCatch() { try { methodThatThrowsThreeExceptions(); } catch (ExceptionOne | ExceptionTwo | ExceptionThree e) { // log and deal with all Exceptions } } public void newMultiMultiCatch() { try { methodThatThrowsThreeExceptions(); } catch (ExceptionOne e) { // log and deal with ExceptionOne } catch (ExceptionTwo | ExceptionThree e) { // log and deal with ExceptionTwo and ExceptionThree } }
More precise rethrow
The Java SE 7 compiler performs more precise analysis of rethrown exceptions, enabling you to specify more specific exception types in the throws clause of a method declaration than are used in the catch/throws of the method body.
Before, we had to do something like this:
static class FirstException extends Exception { } static class SecondException extends Exception { } public void rethrowException_PreJava7(String exceptionName) throws Exception { try { if (exceptionName.equals("First")) { throw new FirstException(); } else { throw new SecondException(); } } catch (Exception e) { throw e; } }
Note how we had to declare the more generic Exception in our throws clause. In Java7, we can change this to:
public void rethrowException_PostJava7(String exceptionName) throws FirstException, SecondException { try { if (exceptionName.equals("First")) { throw new FirstException(); } else { throw new SecondException(); } } catch (Exception e) { throw e; } }
The diamond operator
The diamond operator simplifies constructor calls involving generics. Say you wanted to create a map of String IDs to trade Objects. The old way would have been:
Map<String, Trade> trades = new TreeMap<String, Trade>();
But the right-hand side seems a bit redundant. Could the compiler infer the types by looking at the left-hand-side declaration? In Java7 it can, and the code becomes:
Map<String, Trade> trades = new TreeMap<>();
This is a nice but minor convenience – it could already be nicely dealt with using the Guava library. For example:
Map<String, Trade> trades = Maps.newTreeMap();
Binary integer literals
Useful if you are dealing with binary in your code. For example, you can now do:
int three = 0b101;
Note the 0b (or 0B) prefix to identify the number as a binary literal.
Allowing underscores in numeric literals
I don’t think this one is too significant either, but could be useful in some circumstances. e.g. :
long creditCardNumber = 1234_5678_9012_3456L;
Fork & Join / Concurrency utilities (JSR 166)
The Fork/Join framework focuses on using all the processing resources available in the machine to improve the performance of the applications. It is designed for work that can be broken into smaller pieces recursively (‘Divide and Conquer’ algorithms).
Summary:
- Fork/Join Framework: allows for easier Parallel Programming in Java
- Targeted at multi-processor systems (really almost all hardware today)
Use in situations where:
- a batch of work can broken into smaller recursive calls.
- also uses a work-stealing algorithm where threads with no work can steal available work from other threads that are busy
Written by Doug Lea:
- Creator of oswego package (became java.util.concurrent)
- Contributor to Java Concurrency in Practice
Brief History
Java 1.4 and before: Developing concurrent classes was very difficult — the low-level concurrency primitives provided (synchronized
, volatile
, wait()
, notify()
, and notifyAll()
) were difficult to use correctly, and errors using these facilities difficult to detect and debug
Java 5: Included a new package of concurrency utilities
Task Scheduling Framework – The Executor
framework
Executor: An object that executes submitted Runnable
tasks.
ExecutorService: An Executor
that provides methods that produce a Future
that represents (as yet to be completed) result of an asynchronous computation.
Java 7: ForkJoinPool: a new type of ExecutorService, which
allows you to more easily break up processing to be executed concurrently, and recursively executes ForkJoinTasks:
A thread-like entity that is much lighter weight than a normal thread. Huge numbers of tasks and subtasks may be hosted by a small number of actual threads in a ForkJoinPool.
The new ForkJoinPool employs a divide and conquer algorithm.
Pseudocode (from Doug Lea’s paper on the subject):
Result doWork(Work work) { if (work is small) { process the work } else { split up work invoke framework to solve both parts } }
Summary
- nice to see continued advancement in Java in the concurrency space to build on all the goodies we got in Java 5
- potential uses of this framework are limited and require a fairly narrow problem scope.
New Input/Output features
Java7 introduced a new file I/O library to enhance platform independence and add support for metadata and symbolic links. The new packages are java.nio.file and java.nio.file.attribute. The primary new classes are:
- Path: “a programmatic representation of a path in the file system”. This is probably the new class (interface) that developers will use most often. The file referred by the path does not need to exist. For all practical purposes, you can think of replacing java.io.File with java. io.Path. It includes various methods that can be used to obtain information about the path, including creating, converting and comparing paths.
- Files: The Files class offers a large number of static methods for file related operations e.g. reading, writing, and manipulating files and directories. The Files methods work on instances of Path objects.
That’s it! A summary of what Java7 gave us. See my next blog post for a review of some of the upcoming features in Java8.