TABLE OF CONTENTS (HIDE)

JDK 7 New Features

Java SE 7 (or JDK 7), codenamed Dolphin, was released on July 77, 2011.

JDK 7 New Language Features

There are many small language changes (grouped under a project named Coin):

  • Strings in switch statement.
  • Binary integer literals.
  • Allowing underscores in numeric literals.
  • Catching multiple exception types and rethrowing exceptions with improved type checking.
  • Automatic resource management in try-statement.
  • Improved type inference for generic instance creation, aka the diamond operator <>.
  • Simplified varargs method declaration.

Strings in switch's Selector

Before JDK 7, only integral types (such as int, char) can be used as selector for switch-case statement. In JDK 7, you can use a String object as the selector. For example,

String day = "SAT";
switch (day) {   // switch on String selector
   case "MON": System.out.println("Monday"); break;
   case "TUE": System.out.println("Tuesday"); break;
   case "WED": System.out.println("Wednesday"); break;
   case "THU": System.out.println("Thursday"); break;
   case "FRI": System.out.println("Friday"); break;
   case "SAT": System.out.println("Saturday"); break;
   case "SUN": System.out.println("Sunday"); break;
   default: System.out.println("Invalid");
}

String.equals() method is used in comparison, which is case-sensitive. Java compiler can generate more efficient code on switch than nested if-else statement.

This feature is handy in handling options specified in command-line arguments, which are Strings. For example, the following code is slightly neater code than using nested if-else statement:

/**
 * This program accepts three command-line options
 *   -c : create
 *   -v : verbose
 *   -d : debug
 * More than one options can be specified in any order.
 */
public class JDK7SwitchOnStringTest {
   public static void main(String[] args) {
      boolean create = false;
      boolean verbose = false;
      boolean debug = false;

      for (String arg: args) {
         switch (arg) {
            case "-c": create = true; break;
            case "-v": verbose = true; break;
            case "-d": debug = true; break;
            default:
               System.out.println("invalid option");
               System.exit(1);
         }
      }

      System.out.println("create: " + create);
      System.out.println("verbose: " + verbose);
      System.out.println("debug: " + debug);
   }
}
> javac JDK7SwitchOnStringTest.java

> java JDK7SwitchOnStringTest -c -d
create: true
verbose: false
debug: true

Binary Integer Literals with Prefix "0b" and Underscore in Numeric Literals

In JDK 7, you can express literal values in binary with prefix '0b' (or '0B') for integral types (byte, short, int and long), similar to C/C++ language. Before JDK 7, you can only use octal values (with prefix '0') or hexadecimal values (with prefix '0x' or '0X').

You are also permitted to use underscore (_) to break the digits to improve the readability but you must start and end with a digit, e.g.,

int number1 = 0b01010000101000101101000010100010;
int number2 = 0b0101_0000_1010_0010_1101_0000_1010_0010;
int number3 = 2_123_456;  // break the digits with underscore

For example,

public class BinaryIntegerLiteralTest {
   public static void main(String[] args) {
      // Some 32-bit 'int' literal values
      int anInt1 = 0b0101_0000_1010_0010_1101_0000_1010_0010;
      int anInt2 = 0b0011_1000;
 
      // An 8-bit 'byte' literal value. By default, literal values are 'int'.
      // Need to cast to 'byte'
      byte aByte = (byte)0b0110_1101;
 
      // A 16-bit 'short' literal value
      short aShort = (short)0b0111_0101_0000_0101;
 
      // A 64-bit 'long' literal value. Long literals requires suffix "L".
      long aLong = 0b1000_0101_0001_0110_1000_0101_0000_1010_0010_1101_0100_0101_1010_0001_0100_0101L;
 
      // Formatted output: "%d" for integer in decimal, "%x" in hexadecimal, "%o" in octal.
      // Take note that "%b" prints true or false (for null), NOT binary.
      System.out.printf("%d(%x)(%o)(%b)\n", anInt1, anInt1, anInt1, anInt1);
      System.out.printf("%d(%x)(%o)(%b)\n", aByte, aByte, aByte, aByte);
   }
}
1352847522(50a2d0a2)(12050550242)(true)
109(6d)(155)(true)

In JDK 7, you could insert underscore (_) in between the digits in a numeric literals (integral and floating-point literals) to improve readability. For example,

int anInt = 0b10101000_01010001_01101000_01010001;
double aDouble = 3.1415_9265;
float  aFloat = 3.14_15_92_65f;

Catching Multiple Exception Types and Rethrowing Exceptions with Improved Type Checking

In JDK 7, a single catch block can handle more than one exception types.

For example, prior to JDK 7, you need two catch blocks to catch two exception types although both perform identical task:

try {
   ......
} catch(ClassNotFoundException ex) {
   ex.printStackTrace();
} catch(SQLException ex) {
   ex.printStackTrace();
}

In JDK 7, you could use one single catch block, with exception types separated by a vertical bar (|).

try {
   ......
} catch(ClassNotFoundException|SQLException ex) {
   ex.printStackTrace();
}

[TODO] A complete example on file IO.

Automatic Resource Management in try-with-resources Statement

Before JDK 7, we need to use a try-catch-finally statement to manage resources. We need a finally block, to ensure that the resources are properly closed regardless of whether the try completes normally or abruptly. The code is messy! For example,

import java.io.FileReader;
import java.io.FileWriter;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;

// Copy from one file to another file line by line.
// Pre-JDK 7 requires you to close the resources using a finally block.
public class FileCopyPreJDK7 {
   public static void main(String[] args) {
      BufferedReader src = null;
      BufferedWriter dest = null;
      try {
         src  = new BufferedReader(new FileReader("in.txt"));
         dest = new BufferedWriter(new FileWriter("out.txt"));
         String line;
         while ((line = src.readLine()) != null) {
            System.out.println(line);
            dest.write(line);
            dest.newLine();  // write a newline
         }
      } catch (IOException ex) {
         ex.printStackTrace();
      } finally {            // always close the streams
         try {
            if (src != null) src.close();  // close() throw IOException
            if (dest != null) dest.close();
         } catch (IOException ex) {
            ex.printStackTrace();
         }
      }
 
      try {
         src.read();   // Trigger IOException: Stream closed
      } catch (IOException ex) {
         ex.printStackTrace();
      }
   }
}

JDK 7 introduces a try-with-resources statement, which ensures that each of the resources in try(resources) is properly closed at the end of the statement. This results in cleaner codes.

import java.io.FileReader;
import java.io.FileWriter;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;

// Copy from one file to another file line by line.
// JDK 7 has a try-with-resources statement, which ensures that
//   each resource opened in try(resources) is closed at the end of the statement.
public class FileCopyJDK7 {
   public static void main(String[] args) {
      try (BufferedReader src  = new BufferedReader(new FileReader("in.txt"));
           BufferedWriter dest = new BufferedWriter(new FileWriter("out.txt"))) {
         String line;
         while ((line = src.readLine()) != null) {
            System.out.println(line);
            dest.write(line);
            dest.newLine();
         }
      } catch (IOException ex) {
         ex.printStackTrace();
      }
      // src and dest automatically close.
      // No need for finally to explicitly close the resources.
   }
}

Improved Type Inference for Generic Instance Creation with the Diamond Operator <>

For example,

import java.util.*;
public class JDK7GenericTest {
   public static void main(String[] args) {
      // Pre-JDK 7
      List<String> lst1 = new ArrayList<String>();
      // JDK 7 supports limited type inference for generic instance creation with diamond opeator <>
      List<String> lst2 = new ArrayList<>();

      lst1.add("Mon");
      lst1.add("Tue");
      lst2.add("Wed");
      lst2.add("Thu");

      for (String item: lst1) System.out.println(item);
      // Mon
      // Tue
      for (String item: lst2) System.out.println(item);
      // Wed
      // Thu

      List<String> lst3 = List.of("Fri", "Sat");  // JDK 9
      System.out.println(lst3);
      // [Fri, Sat]
      System.out.println(Arrays.toString(lst3.toArray()));
      // [Fri, Sat]
      lst3.forEach(System.out::println);   // JDK 8
      // Fri
      // Sat
   }
}

Simplified varargs Method Declaration with @SafeVarargs Annotation

In JDK 7, you have the option of using @SafeVarargs annotation to suppress the warning you get when compiling a method with a non-reifiable varargs parameter. This annotation should be used when the method ensures that only elements of the same type as the varargs parameter are stored in the varargs array.

Example [TODO]

JDK 7 Library Changes

New File IO Libraries (JSR 203)

JDK 7 added support for multiple file systems, file metadata and symbolic links in new packages java.nio.file, java.nio.file.attribute and java.nio.file.spi.

Concurrency Utilities (JSR 166)
Timsort is used to sort collections and arrays of objects instead of merge sort
Library-level support for elliptic curve cryptography algorithms
Enhanced library-level support for new network protocols, including SCTP and Sockets Direct Protocol

JDK 7 Other New Features

JVM support for Dynamic Languages (JSR 292)
Upstream updates to XML and Unicode

REFERENCES & RESOURCES