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

JDK 7 introduces a few 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 re-throwing exceptions with improved type checking.
  • Automatic resource management in try-resource statement.
  • Improved type inference for generic instance creation, aka the diamond operator <>.
  • Simplified varargs method declaration.

Strings in switch's Selector

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

public class J7SwitchStringTest {
   public static void main(String[] args) {
      String day = "SAT";
      switch (day) {  // switch on String selector (JDK 7)
         case "MON":
         case "TUE":
         case "WED":
         case "THU":
            System.out.println("Working Day");
            break;
         case "FRI":
            System.out.println("Thank God It's Friday");
            break;
         case "SAT":
         case "SUN":
            System.out.println("Gone Fishing");
            break;
         default:
            System.out.println("Invalid");
      }
   }
}

String's .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,

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

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

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

From JDK 7, you can express literal values in binary with prefix '0b' (or '0B') for integral types, 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'). From JDK 7, you are also permitted to use underscore (_) to break the digits to improve the readability but you must start and end with a digit. For examples,

int anInt1 = 0b01010000101000101101000010100010;    // binary literal prefix with '0b' or '0B'
int anInt2 = 0b0101_0000_1010_0010_1101_0000_1010_0010;  // break digits with underscore for readability
int anInt3 = 2_123_456;  // break the digits with underscore
byte aByte = (byte)0b0110_1101;  // '0b' for int(32-bit), need to cast to byte(8-bit)/short(16-bit)
short aShort = (short)0b0111_0101_0000_0101;
long aLong = 0b1000_0101_0001_0110_1000_0101_0000_1010_0010_1101_0100_0101_1010_0001_0100_0101L;
      // long(64-bit) with suffix 'L'
double aDouble = 3.1415_9265;  // You can also use underscore on fractional part for readability
float aFloat = 3.14_15_92_65f;

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)

Catching Multiple Exception Types and Re-throwing Exceptions with Improved Type Checking

Prior to JDK 7, you need two catch blocks to catch two exception types even though both perform identical task. For example,

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

From JDK 7, you could use one single catch block to handle more than one exception types, with the exception types separated by a vertical bar (|). For example,

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

[TODO] A complete example on file IO.

Automatic Resource Management in try-with-resources Statement

Prior to 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 PreJ7FileCopy {
   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 J7FileCopy {
   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 J7GenericTest {
   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 operator <>
      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