TABLE OF CONTENTS (HIDE)

JDK 9 New Features

Introduction

References:
  1. What's New in Oracle JDK 9 @ https://docs.oracle.com/javase/9/whatsnew/toc.htm.

 

JDK 9 comes with these big new features (plus some minor new features and enhancements):

  1. Java Module System (Project Jigsaw)
  2. JDK 9 REPL (Read Evaluate Print Loop) - jshell
  3. Some language changes (JEP 213: Milling Project Coin)
  4. ......

Java Module System - Project Jigsaw

References:
  1. Project Jigsaw: Module System Quick-Start Guide @ http://openjdk.java.net/projects/jigsaw/quick-start.
  2. Project Jigsaw @ http://openjdk.java.net/projects/jigsaw/.
  3. JSR 376: Java Platform Module System @ http://openjdk.java.net/projects/jigsaw/spec/.
  4. JEP 200: The Modular JDK @ http://openjdk.java.net/jeps/200.
  5. JEP 201: Modular Source Code @ http://openjdk.java.net/jeps/201.
  6. JEP 220: Modular Run-Time Images @ http://openjdk.java.net/jeps/220.
  7. JEP 260: Encapsulate Most Internal APIs @ http://openjdk.java.net/jeps/260.
  8. JEP 261: Modular System @ http://openjdk.java.net/jeps/261.
  9. JEP 281: jlink: The Java Linker @ http://openjdk.java.net/jeps/281.
  10. JDK 9 Modules @ https://www.journaldev.com/13106/java-9-modules.

 

The most notable and drastic change in JDK 9 is certainly the "Java Modular System", which is carried out through the Project Jigsaw @ http://openjdk.java.net/projects/jigsaw/.

The current Java System suffers from these problems:

  • The JDK is too big, with many legacy codes. This also impedes on the performance, test and maintenance.
  • JAR files like rt.jar are too big to be used in small devices.
  • There is no strong encapsulation. The public access is too open. Some internal non-critical APIs like sun.*, *.internal.*, are accessible, as they are public, which poses security issues.

I shall assume that you are familiar with Java package and CLASSPATH. Otherwise, read "Package and CLASSPATH".

Why module?

A module is a set of related packages and resources (code and data) that is designed for reuse. It adds a higher level of aggregation above the package. Each module has a module descriptor called module-info.java (to be compiled into module-info.class).

Before JDK 9, public classes are accessible to ALL, resulted in many APIs got exposed unnecessarily. With "modules", you can conceal "packages" for internal use, or export "packages" to be used by other modules. You can further specify which classes within a package are to be shared with which modules, or you can set them to be accessible by any modules. In other words, you can fine-tune the public access restriction of packages and classes to improve security with strong encapsulation and stability with reliable dependencies. "public" on a class declaration no longer means accessible to everyone.

Module Dependency by Examples

Example 1: Hello Module - The "module" Statement

I shall assume that our project's base directory is "c:\myProject", with a sub-directory "src" for the source files and a sub-directory "mods" for the compiled modules (instead of "classes" for compiled classes). Create the directory structure.

Let's write a module called "com.hello". By convention, the module's directory name is the same as the module's name. Create a sub-directory "com.hello" under "src".

Each module has a module descriptor file called "module-info.java" under the top level directory of a module. Create the module descriptor "module-info.java" under "src\com.hello", as follows:

// src\com.hello\module-info.java
module com.hello {
}

This module com.hello contains a package com.hellopack, with a class Hello (i.e., com.hellopack.Hello). Create the directory structure "src\com.hello\com\hellopack" for the package com.hellopack under the module.

Write the main class com.hellopack.Hello in "src\com.hello\com\hellopack\Hello.java", as follows:

// src\com.hello\com\hellopack\Hello.java
package com.hellopack;
public class Hello {
   public static void main(String[] args) {
      System.out.println("hello, module");
   }
}

We shall keep our compiled module in a sub-directory called mods (under c:\myProject). Our compiled module com.hello shall be kept in directory mods\com.hello.

Compile the module as follows, where -d option specifies the directory of the generated class file, which will be created automatically.

> c:
> cd \myProject
> javac -d mods/com.hello src/com.hello/module-info.java src/com.hello/com/hellopack/Hello.java
   // Note: You can use either forward slash (Unix and Mac) or 
   //       backward slash (Windows) as path separator in the javac command

The mods\com.hello\module-info.class and mods\com.hello\com\hellopack\Hello.class shall be generated, as shown:

> tree /f /a
+---mods
|   +---com.hello
|       |   module-info.class
|       |
|       \---com
|           \---hellopack
|                   Hello.class
\---src
    +---com.hello
        |   module-info.java
        |
        \---com
            \---hellopack
                    Hello.java

You can run the main class as follows, where --module-path (or -p) specifies the module path, -m specifies the main class. Both -p and -m are new options in JDK 9:

> c:
> cd \myProject
> java --module-path mods -m com.hello/com.hellopack.Hello
hello, module

Notes:

  • JDK 9 introduces module-path for locating module, similar to classpath for locating classes prior to JDK 9.
  • In this example, I named the module and package differently for academic purpose. In practice, the module and package could be given the same name, i.e., com.hello. In the next example, I shall use the same name for the module and the package.
Example 2: Module Dependency - "exports package" and "requires module" Directives

Let's create two modules: com.hi and com.world, such that com.hi depends on (or reads) com.world.

We need to start with the independent module com.world. Create the module directory "com.world" under "src". Create the module descriptor module-info.java under "src\com.world", as follows:

// src\com.world\module-info.java
module com.world {
   exports com.world;  // This "package" can be used by ALL other modules
}

The com.world module contains a package called com.world (having the same name as the module), which is exported to be used by ALL other modules.

The package com.world contains a class called World (i.e., com.world.World), as follows:

// src\com.world\com\world\World.java
package com.world;

public class World {
   public static String getWorld() {
      return "world";
   }
}

Compile the module com.world as follows:

> cd <project-base-path>
> javac -d mods/com.world src/com.world/module-info.java src/com.world/com/world/World.java

Now, let us write the dependent module com.hi, Create the module directory "com.hi" under "src". Create the module descriptor module-info.java under "src\com.hi", as follows:

// src\com.hi\module-info.java
module com.hi {
   requires com.world;   // Depends on this "module"
                         // "requries" is analogous to "imports"
}

The module com.hi contains a package com.hi (having the same name as the module), with a class com.hi.Hello. The Hello class invokes method in the World class (of the module com.world):

// src\com.hi\com\hi\Hello.java
package com.hi;

import com.world.World;  // package and class name

public class Hello {
   public static void main(String[] args) {
      System.out.println("hello, " + World.getWorld());
   }
}

Compile the module com.hi as follows (where -p is the same as --module-path):

> cd <project-base-path>
> javac -p mods -d mods/com.hi src/com.hi/module-info.java src/com.hi/com/hi/Hello.java

Run the Hello class:

> java -p mods -m com.hi/com.hi.Hello
hello, world

The directory/file structure is as follows:

> tree /f /a
+---mods
|   |
| +---com.hi
| | | module-info.class
| | |
| | \---com
| | \---hi
| | Hello.class
| |
| \---com.world
| | module-info.class
| |
| \---com
| \---world
| World.class
|
\---src |
+---com.hi
| | module-info.java
| |
| \---com
| \---hi
| Hello.java
|
\---com.world
| module-info.java
|
\---com
\---world
World.java
Example 3: Packaging in Modular JAR Files

For deployment, we can package a module as a "Modular JAR file", which is a regular JAR file with a module descriptor module-info.class in its top-level directory.

We shall keep our modular JAR files in a sub-directory "mlib". Create a sub-directory "mlib" under "c:\myProject".

Create modular JAR file for module com.world as follows, where --create (or -c) for creating new JAR file, --file (or -f) specifies the name of the JAR file, -C to change the directory:

> cd <project-base-path>
> jar --create --file mlib/com.world.jar --module-version 1.0 -C mods/com.world .

Create modular JAR file for module com.hi as follows, where --main-class specifies the main entry class of the module:

> jar -c -f mlib/com.hi.jar --main-class com.hi.Hello -C mods/com.hi .

You can run the modular JAR file directly as follows, where -p (or --module-path) specifies the module path, -m (or --module) specifies the module to be executed (both -p and -m are new options in JDK 9).

> java -p mlib -m com.hi
hello, world
Missing "requires module" Directive

Comment out the requires directive in com.hi's module descriptor src\com.hi\module-info.java; and re-compile:

> javac -p mods -d mods/com.hi src/com.hi/module-info.java src/com.hi/com/hi/Hello.java
src\com.hi\com\hi\Hello.java:4: error: package com.world is not visible
import com.world.World;
          ^
  (package com.world is declared in module com.world, but module com.hi does not read it)
Missing "exports package"Directive

Now commmet out the exports directive in com.world's module descriptor src\com.world\module-info.java; and re-compile:

> javac -d mods/com.world src/com.world/module-info.java src/com.world/com/world/World.java

> javac -p mods -d mods/com.hi src/com.hi/module-info.java src/com.hi/com/hi/Hello.java
src\com.hi\com\hi\Hello.java:4: error: package com.world is not visible
import com.world.World;
          ^
  (package com.world is declared in module com.world, which does not export it)
CLASSPATH vs MODULEPATH

A CLASSPATH is a sequence of classes' base paths and JAR files for the Java Compiler or JVM to locate the classes used in the application.

Similarly, in JDK 9, a MODULEPATH is a sequence of modules' base paths and Modular JAR files for the Java Compiler or JVM to locate the modules used in the application.

Revisit "Module"

A module adds a higher level of aggregation above package. It is a set of related packages, as well as resources (such as images and XML files). By convention, a module is kept in a directory same as the module name. Each module contains a module descriptor module-info. The descriptor contains directives (such as requires, exports, uses, provides...with, opens, to and transistive) to describe:

  • the module's name
  • the module's dependencies (via "requires module")
  • the packages it explicitly makes available to other modules (via "exports package")
  • the services it provides (via provides...with directive)
  • the services it consumes (via uses directive)
  • to what other modules it allows reflection (via opens directive)
// moduleName\module-info.java
module moduleName {
   ......  // module directives
}
requires <moduleName>

A requires directive specifies that this module depends on another module. When module A requires module B, module A is said to read module B; and module B is read by module A.

requires transitive <moduleName>

Other modules that read this module also read this dependency (known as implied readability). For example, if module A requires transistive module B, and module C requires module A. Module C can read module B without explicitly requires module B.

exports <packageName>

Specify a package (of this module) whose public types are accessible to ALL other modules.

exports <packageName> to <modules>

Further specify, in a comma-separated list, which module's code can access the exported package. For example, [TODO]

uses <serviceName>

Specify a service used by this module - making this module a service consumer. A service is an instance of a class that implements the interface or extends the abstract class specified in the uses directive.

provides <serviceName> with <implementationClass>

Specify that a module provides a service implementation - making this module a service provider.

opens <packageName>

Before JDK 9, reflection could be used to learn about all types in a package and all members of a type - even its private members. In JDK 9, the opens packageName specifies that the package’s public types are accessible to code in ALL other modules at runtime only. Also, all the types in the specified package (and all of the types’ members) are accessible via reflection.

opens <packageName> to <modules>

To the specified modules (commas-separated list) only.

Service Consumer and Service Provider Modules By Example

Reference: JDK 9 Modules - Creating Services and Service Providers @ https://www.logicbig.com/tutorials/core-java-tutorial/modules/services.html.

 

Services allow for loose coupling between service consumers modules and service providers modules.

A service consumer module declares that it uses one or more interfaces whose implementations will be provided at runtime by some service provider module. A service provider module declares what implementations of service interface it provides...with.

The Service Consumer Module

Let's call our service consumer module com.hello.consumer. It contains a package com.hello.consumer, and consume a service defined in interface com.hello.consumer.HelloService (to be provided by a service provider).

// com.hello.consumer's Module Descriptor
// src\com.hello.consumer\module-info.java
module com.hello.consumer {
   exports com.hello.consumer;
   uses com.hello.consumer.HelloService;  // service's interface
}
// Service Interface
// src\com.hello.consumer\com\hello\consumer\HelloService.java
package com.hello.consumer;

import java.util.Iterator;
import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;

public interface HelloService {

   // Return a List of implementations for this service.
   public static List<HelloService> getInstances() {
      ServiceLoader<HelloService> services = ServiceLoader.load(HelloService.class);
      Iterator<HelloService> iter = services.iterator();
      if (!iter.hasNext()) throw new RuntimeException("No service providers found!");
      List<HelloService> lst = new ArrayList<>();
      iter.forEachRemaining(lst::add);
      return lst;
   }

   void sayHello(String msg);  // to be provided by the implementation
}
// Compile the module from "src" into "mods"
> cd <project-base-directory>
> javac -d mods/com.hello.consumer src/com.hello.consumer/module-info.java src/com.hello.consumer/com/hello/consumer/HelloService.java

// Create Modular JAR file in "mlib"
> jar --create --file mlib/com.hello.consumer.jar -C mods/com.hello.consumer .
How does ServiceLoader find a class?

The ServiceLoader can find and instantiate classes that implement a specific interface. When we call the static method ServiceLoader.load(interfaceName), it returns a list of classes that implement this interface. The JDK 9's module declares the services that a ServiceLoader can load, and a module can also declare what services it may need to load via the ServiceLoader.

The Service Provider Module

Our service provider module called com.hello.provider, provides an implementation for the service.

// com.hello.provider Module Descriptor
// src\com.hello.provider\module-info.java
module com.hello.provider {
   requires com.hello.consumer;
   provides com.hello.consumer.HelloService with com.hello.provider.HelloServiceImpl;
      // provides an implementation for the service.
}
// Implementation of the service
// src\com.hello.provider\com\hello\provider\HelloServiceImpl.java
package com.hello.provider;

import com.hello.consumer.HelloService;

public class HelloServiceImpl implements HelloService {
   @Override
   public void sayHello(String msg) {
      System.out.println("hello, " + msg);
   }
}
// Compile the module from "src" into "mods"
> cd <project-base-directory>
> javac -p mlib -d mods/com.hello.provider src/com.hello.provider/module-info.java src/com.hello.provider/com/hello/provider/HelloServiceImpl.java

// Create Modular JAR file in "mlib"
> jar --create --file mlib/com.hello.provider.jar -C mods/com.hello.provider .
Service Client Module

Now, write a client module that invokes the service.

// com.hello.client Module Descriptor
// src\com.hello.client\module-info.java
module com.hello.client {
   requires com.hello.consumer;
}
// src\com.hello.client\com\hello\client\Main.java
package com.hello.client;

import com.hello.consumer.HelloService;

import java.util.List;

public class Main {
   public static void main(String[] args) {
      List<HelloService> services = HelloService.getInstances();
      for (HelloService service : services) {
         System.out.println(service.getClass().getName());  // print the implementation classname
         service.sayHello("service");
      }
   }
}
// Compile the module
> javac -p mlib -d mods/com.hello.client src/com.hello.client/module-info.java src/com.hello.client/com/hello/client/Main.java

// Create JAR
> jar --create --file mlib/com.hello.client.jar --main-class=com.hello.client.Main -C mods/com.hello.client .

// Run
> java -p mlib -m com.hello.client
com.hello.provider.HelloServiceImpl
hello, service

JEP 282: jlink - The Java Linker

The goal of this JEP is to "create a tool that can assemble and optimize a set of modules and their dependencies into a custom run-time image". jlink is the linker tool and can be used to link a set of modules, along with their transitive dependences, to create a custom modular run-time image (directory).

The following example creates a run-time images that contains the module com.hello.client and its transitive dependences:

// For Windows
> cd <project-base-path>
> jlink --module-path "%JAVA_HOME%/jmods";mlib --add-modules com.hello.client --output helloapp

// For Unixes MacOS X
$ cd <project-base-path>
$ jlink --module-path $JAVA_HOME/jmods:mlib --add-modules com.hello.client --output helloapp

Notes:

  • The module path "%JAVA_HOME%/jmod" is the directory containing java.base.jmod and other standard and JDK modules.
  • The module path "mlib" is the directory containing our module com.hello.client.
  • The module com.hello.client shall be packaged in modular JAR or JMOD format (in the module path).

The output "helloapp" is a directory (36 MB), as follows:

> tree /a helloapp
HELLOAPP
+---bin
|   \---server
+---conf
|   \---security
|       \---policy
|           +---limited
|           \---unlimited
+---include
|   \---win32
+---legal
|   \---java.base
\---lib
    +---security
    \---server

The modules are kept under "helloapp\lib\modules" (22 MB)?!

To run the Main class:

// Run the main class
> cd <project-base-path>
> helloapp\bin\java -m com.hello.client/com.hello.client.Main
Exception in thread "main" java.lang.RuntimeException: No service providers found!

// Re-link with provider module
> jlink --module-path "%JAVA_HOME%/jmods";mlib --add-modules com.hello.client,com.hello.provider --output helloapp

// Run the main class again
> helloapp\bin\java -m com.hello.client/com.hello.client.Main
com.hello.provider.HelloServiceImpl
hello, service

JDK 9 Introduces a new optional phase, link time, which is in-between compile time and run time, during which a set of modules can be assembled and optimized into a custom runtime image.

jmod Tool and jmod Files

JDK 9 Introduces the JMOD format, which is a packaging format similar to JAR except it can include native code and configuration files. It also introduces a jmod tool for creating jmod files.

[TODO]

JEP 200: The Modular JDK

JDK has grown over the years, with massive amount of legacy codes. But the Java platform has primarily been a monolithic one-size-fits-all solution.

The goal is to divide the JDK into a set of modules that can be combined at compile time, build time and run time into a variety of configurations.

The modular JDK implements these design principles:

  1. Standard modules, whose specifications are governed by the JCP (Java Community Process), start with "java.".
    • Base Module java.base: defines the foundational APIs of the Java SE Platform, including core JDK packages such as java.lang, java.util, java.io, java.nio, java.net, javax.net, java.text, java.security, javax.cryptio.
      The Base Module is an independent module and does NOT depends on any other modules. By default, all other modules depends on this module.
      The Base Module is also the default module for all JDK modules and user-defined modules.
    • Module java.desktop: Defines the AWT and Swing user interface toolkits, plus APIs for accessibility, audio, imaging, printing, and JavaBeans, including packages java.awt, java.awt.event, java.awt.*, java.beans, javax.imageio, javax.print, javax.swing, javax.swing.*, javax.sound.*.
    • Module java.sql: defines the JDBC API, including packages java.sql, javax.sql.
    • Others
  2. All other modules that are part of JDK, start with "jdk.".
  3. JavaFX modules, starts with "javafx.".

A new option --list-modules is provided in java tool to list all the modules, for example,

> java --list-modules
java.activation@10.0.1
java.base@10.0.1
......

JEP 201: Modular Source Code

The current source code JAR files are too big, especially rt.jar. The goal of this JEP is to "reorganize the JDK source code into modules, enhance the build system to compile modules, and enforce module boundaries at build time".

JEP 220: Modular Run-Time Images

The main goal of this JEP is to "restructure the JDK and JRE run-time images to accommodate modules and to improve performance, security, and maintainability".

The JDK 9 does not have a "jre" sub-directory. But it has a "jmods" sub-directory, which contains the Java modules (such as java.activation.jmod, java.base.jmod, etc.). In other words, In JDK 9, JRE is separated into a separate distribution jmods.

JDK 9 removes rt.jar or tools.jar.

JEP 260: Encapsulate Most Internal APIs

The main goal of this JEP is to "encapsulate most of the JDK's internal APIs by default so that they are inaccessible at compile time, and prepare for a future release in which they will be inaccessible at run time".

JDK 9 REPL - jshell

JDK 9 introduces a new tool called jshell to support REPL (Read Evaluate Print Loop). It is used to execute and test any Java constructs like class, interface, enum, object, statements etc. For example,

> jshell
|  Welcome to JShell -- Version 10.0.1
|  For an introduction type: /help intro

jshell> int i = 123
i ==> 123

jshell> i + 456
$2 ==> 579

jshell> System.out.println("hello")
hello

jshell> for (var i = 0; i < 3; ++i)
   ...>    System.out.println(i*i)
0
1
4

jshell> /exit
|  Goodbye

New Java Language Features

Reference: Java Language Updates Java SE 9 @ https://docs.oracle.com/javase/9/language/toc.htm.

 

JDK 9 introduces a few new language features, under "JEP 213: Milling Project Coin".

private and private static methods in the interface

Prior to Java 8, an interface can have only two kinds of entities:

  1. public static final variables (constants)
  2. public abstract methods: method signature only without implementation.

You need to use an abstract class if you have non-abstract methods with implementations.

Java 8 introduces public default and public static methods into the interface. You could provide implementations in these methods.

JDK 9 supports private methods and private static methods. In JDK 9, an interface can contains six kinds of entities:

  1. public static final variables (constant)
  2. public abstract methods
  3. public default methods
  4. public static methods
  5. private methods: meant for helper method accessible wtihin the current interface only, so as to remove redundant codes. Not inherited by its subclasses and cannot be overridden.
  6. private static methods: class helper method accessible within the current interface only.

try-with-resources Improvements

Before Java 7, you need to use a try-catch-finally construct to manage resource, and manually close all resource in the finally. Java 7 introduces a new try-with-resources construct which automatically closes all resources at the end of the statement. Read HERE.

If you already have a resource as a final or "effectively final" variable, you can use that variable in a try(resources) clause without declaring a new variable. An "effectively final" variable is one whose value is never changed after it is initialized. 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.
// Java 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 FileCopyJava9 {
   public static void main(String[] args) throws IOException {
      BufferedReader src  = new BufferedReader(new FileReader("in.txt"));
      BufferedWriter dest = new BufferedWriter(new FileWriter("out.txt"));

      try (src; dest) {   // This is permitted in JDK 9
         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.
   }
}

Before JDK 9, you need to create new variables:

......
try (BufferedReader src1 = src;
     BufferedWriter dest1 = dest) {
   ......
}

[TODO] Write a better example.

Other Minor Language Changes

@SafeVargs annotation is allowed on private instance methods

The @SafeVarargs annotation can be applied only to methods that cannot be overridden. These include static methods, final instance methods, and, new in JDK 9, private instance methods.

Allow the diamond operator with anonymous inner classes

In JDK 9, as long as the inferred type is denotable, you can use the diamond operator <> when you create an anonymous inner class.

Removal of the underscore from the set of legal identifier names

If you use the underscore character ("_") as an identifier, your source code can no longer be compiled.

Other JEPs

JEP 269: Convenience Factory Methods for Collections

Reference: JEP 269 - Convenience Factory Methods for Collections

Motivation

Prior to JDK 9, creating a small, unmodifiable collection of List, Set, or Map involves constructing it, storing it in a local variable, and invoking add() on it several times, and then wrapping it. For example,

List lst1 = new ArrayList<>();  // construct and store in a local variable
lst1.add("apple1");             // several add()'s
lst1.add("orange1");
lst1.add("banana1");
lst1 = Collections.unmodifiableList(lst1);  // wrap it

Alternatively, you could populate a collection using a "copy constructor" from another collection, for example,

List lst2 = Collections.unmodifiableList(
   new ArrayList<>(Arrays.asList("apple2", "orange2", "banana2")));
   // Using the "copy constructor" of ArrayList

Another alternative is to use the so-called "double brace" technique, which uses the instance-initializer construct in an anonymous inner class, for example,

List lst3 = Collections.unmodifiableList(new ArrayList<>() {{
   add("apple3"); add("orange3"); add("banana3");
}});

You could also use the Java 8's Stream API to construct small collections, by combining stream factory methods and collectors, for example,

List lst4 = Collections.unmodifiableList(
   Stream.of("apple4", "orange4", "banana4").collect(Collectors.toList()));
JDK 9 New Features

JDK 9 provides a static factory method called of() in the List, Set, and Map interfaces to simplify creation of unmodifiable instances of these collections. We can write:

List<String> lst = List.of("apple", "orange", "banana");
Set<String> set = Set.of("apple", "orange", "banana");
Map<Integer, String> map = Map.of(1, "apple", 2, "orange", 3, "banana");

For List and Set, the factory methods of() are heavily overloaded for zero to ten elements, plus arbitrary number of elements, as follows:

// List interface's factory method to return an immutable list
static <E> List<E> of​(E... elements)  // an arbitrary number of elements with varargs
static <E> List<E> of​()               // zero elements
static <E> List<E> of​(E e1)           // one element (fixed)
static <E> List<E> of​(E e1, E e2)     // two elements
static <E> List<E> of​(E e1, E e2, E e3)  // three elements
......
static <E> List<E> of​(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10)  // ten elements

// Set interface has a similar set of overloaded methods

The of() includes varargs overloads, so that there is no limit on the size. It is tuned for smaller sizes with fixed-argument overloads for up to ten of elements. While this introduces some clutter in the API, it avoids array allocation, initialization, and garbage collection overhead that is incurred by varargs calls.

For Map, a set of fixed-argument methods are provided for zero to ten elements:

static <K,V> Map<K,V> of​()  // zero elements
static <K,V> Map<K,V> of​(K k1, V v1)  // one element
static <K,V> Map<K,V> of​(K k1, V v1, K k2, V v2)  // two elements
......
static <K,V> Map<K,V> of​(K k1, V v1, K k2, V v2, 
      K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6,
      K k7, V v7, K k8, V v8, K k9, V v9, K k10, V v10)  // ten elements

To support arbitrary number of elements, the Map interface provides a static method called ofEntries(), which takes argument of Map.Entry objects, as follows:

static <K,V> Map<K,V> ofEntries​(Map.Entry<? extends K,? extends V>... entries)

Another staitc method called entry() is also provided to create a Map.Entry object:

static <K,V> Map.Entry<K,V> entry​(K k, V v)  // Returns an immutable Map.Entry containing the given key and value.

For examples,

Map<Integer, String> m1 = Map.of(1, "apple1", 2, "orange2", 3, "banana3");  // three elements (fixed)
 
Map<Integer, String> m2 = Map.ofEntries(  // arbitrary elements
      Map.entry(1, "apple2"),
      Map.entry(2, "orange2"),
      Map.entry(3, "banana2"));

Note that interface's static methods are not inherited, so it will not be possible to invoke them via an implementing subclasses, nor via an instance of the interface type.

Others

Process API Improvements

Added two new interfaces in Process API: java.lang.ProcessHandle and java.lang.ProcessHandle.info.

CompletableFuture API Improvements

Support delays and timeouts, add some utility methods and better sub-classing.

Reactive Streams

JDK 9 Reactive Streams API is a Publish/Subscribe Framework to implement asynchronous, scalable and parallel applications. JDK 9 introduces: java.util.concurrent.Flow, java.util.concurrent.Flow.Publisher, java.util.concurrent.Flow.Subscriber, java.util.concurrent.Flow.Processor.

Optional Class Improvements

JDK 9 added some useful new methods to java.util.Optional class, such as stream().

Stream API Improvements

JDK 9 added four useful new default methods to java.util.Stream interface: dropWhile(), takeWhile().

The takeWhile() takes a predicate as an argument and returns a Stream of subset of the given Stream values until that predicate returns false for first time. If first value does NOT satisfy that predicate, it just returns an empty Stream. For example,

Stream.of(1,2,3,4,5,6,7,8,9,10).takeWhile(i -> i < 5)
Enhanced @Deprecated annotation

Prior to JDK 9, @Deprecated annotation is just a Marker interface without any methods. JDK 9 enhances @Deprecated annotation to provide more information about deprecated API and also provide a tool jdeprscan to analyse an application’s static usage of deprecated APIs. Two methods were added: forRemoval() and since() to serve this information.

HTTP2 Client

HTTP/2 is an improvement over HTTP/1.1:

  • In HTTP/1.1, we cannot have more than six connections open at a time, so every request has to wait for the others to complete. HTTP/2 can send multiple requests for data in parallel over a single TCP connection, known as "HTTP/2 Multiplexing".
  • In HTTP/1.1, every request sent to the server will have the header's additional data, which increases bandwidth. This can be eliminated in HTTP/2.0 by having headers packed into one compressed block that will be sent as a unit, known as "HTTP/2 Header Compression".
  • In an HTTP/1.1, an HTML page is sent to the browser. The browser has to parse it and decide which assets are required, then request those assets from the server. This can be eliminated by "Server Push" in HTTP/2. It allows servers to push responses proactively to the client instead of waiting for the new request to process.
  • Text is replaced by Binary in HTTP/2.
  • Domain sharding and asset concatenation are no longer needed with HTTP/2.

JDK 9 introduces new HTTP2 Client API (under JEP 110: HTTP/2 Client (Incubator)). It supports both HTTP/1.1 and HTTP/2 protocols. It supports both synchronous (blocking mode) and asynchronous modes (using WebSocket API).

Multi-Resolution Image API

JDK 9 added an interface MultiResolutionImage in java.awt.image package, which encapsulates a set of images with different height and widths.

Miscellaneous
  • GC (Garbage Collector) Improvements
  • Stack-Walking API
  • Filter Incoming Serialization Data
  • Deprecate the Applet API (JEP 289: Deprecate the Applet API): The Applet API, which is rapidly becoming irrelevant as web-browser vendors remove support for Java browser plug-ins. JDK 9 marked @Deprecated(since="9") for applet classes, and removed appletviewer tool. Developer should use alternative technologies such as Java Web Start or installable applications.
  • Indify String Concatenation
  • Enhanced Method Handles
  • Java Platform Logging API and Service
  • Compact Strings
  • Parser API for Nashorn
  • Javadoc Search
  • HTML5 Javadoc

REFERENCES & RESOURCES