← Blog

From JDK 8 to 11

2019-11-14 · 5 min read

javajdk

From JDK 8 to 11

Java 8 to 11

After its first beta in 1995 and the 1.0 release in January 1996, Java shipped a new major version roughly every two years — up to Java 6 in 2006. Then came a five-year wait for Java 7 in 2011.

Following the Java 8 release in March 2014, Oracle changed its release policy: it introduced short-term support transition releases alongside long-term support (LTS) versions. Interim releases are supported until the next interim or LTS version ships.

Java 9 arrived in September 2017, Java 10 in March 2018, and the current LTS version, Java 11, landed in September 2018. At the time of writing, Java 13 (released September 2019) was the latest available version.

Below I cover the most notable changes when migrating from Java 8 to Java 11.


Modularization — Project Jigsaw

Introduced with Java 9, Project Jigsaw lets you decompose monolithic applications into discrete modules. Classes that serve the same purpose are grouped under a single source root, forming self-contained modules within the application.

Modularity can be seen as a middle ground between a monolith and a microservice architecture. Each module encapsulates all the layers it needs to fulfill a single responsibility. This makes it easier to later migrate a monolith to microservices.

Inter-module accessibility is declared in a module-info.java file located at the module root.

Suppose the project has two modules: com.demo.sql and com.demo.audit. The sql module has two packages: connection and crud.

If we want to keep connection private to the sql module but expose crud to other modules, module-info.java looks like this:

module sql {
  exports com.demo.sql.crud;
}

If the audit module needs to access classes inside sql.crud, it declares the dependency in its own module-info.java:

module audit {
  requires com.demo.sql.crud;
}

Java 9 ships with roughly 40 standard modules (java.base, java.desktop, java.naming, java.sql, …), visible under $JAVA_HOME/jmods.

For more details, see Project Jigsaw on Baeldung.


JEP 330: Launching Single-File Source Programs

Before Java 11, running a class with a main method from the console required an explicit compile step followed by execution:

class Test {
  public static void main(String... args) {
    System.out.println("Application running...");
  }
}
$javac -d classes Test.java
$java -cp classes Test

With Java 11, you can run a single-file program directly — no separate compile step needed:

$java Test.java

JEP 328: Java Flight Recorder

A low-overhead profiling tool for monitoring JVM applications at runtime.

Some issues — like memory leaks that take days to surface — are difficult to reproduce locally. Flight Recorder captures these events in production and lets you analyze root causes after the fact. Enable it with the -XX:StartFlightRecording JVM flag.


JEP 333: ZGC

A new, scalable, low-latency garbage collector. Key design targets:

  • Heap sizes up to terabytes
  • Maximum GC pause time of 10 ms
  • Maximum throughput overhead of 15%

JEP 286: Local-Variable Type Inference (Java 10)

You can now declare local variables using the var keyword instead of an explicit type:

Where var can be used:

  • Local variables with an initializer
  • Index variables in for loops
var testWord = "Hello, Test";
var age = 22;
var isAvailable = true;
for (var i = 0; i < 10; i++)
for (var user : users)

Where var should NOT be used:

  • Variables declared without an initializer
  • Variables initialized to null
  • Array declarations
  • Method parameters
  • Method return types
var test;
var test = null;
var test = {0, 1, 2};
public void get(var id);
public var get(Long id);

JEP 323: Local-Variable Syntax for Lambda Parameters

Lambda parameters can now use var, enabling annotations in implicit-style lambdas:

(Item a, int b) -> a.create(b);       // Explicit types
(a, b) -> a.create(b);                // Implicit types
(var a, var b) -> a.create(b);        // Implicit with var

Before var, adding an annotation to a lambda parameter required switching to the explicit form. Now var allows annotations in the implicit style:

(@Nonnull var a, var b) -> a.create(b);

Mixing explicit and implicit types in the same lambda parameter list is not allowed:

(Item a, b) -> a.create(b);      // invalid
(var a, b) -> a.create(b);       // invalid
(var a, int b) -> a.create(b);   // invalid

JShell

The REPL (Read-Eval-Print Loop) tool introduced in Java 9. JShell lets you write and execute Java code directly in the terminal — no need to create a file, compile it, and run it separately.

For more information, see the Introduction to JShell in the Oracle documentation.


Optional — New Methods

ifPresentOrElse(): Instead of writing an if-else block around Optional.isPresent(), you can pass both the present and absent handlers directly:

if (optionalPrice.isPresent()) {
  updatePrice(optionalPrice.get());
} else {
  finishOperation();
}

becomes:

optionalPrice.ifPresentOrElse(this::updatePrice, this::finishOperation);

or(): Provides an alternative Optional when the original is empty:

Optional<Person> optionalPerson = findPersonById(1L).or(() -> Optional.of(new Person()));

CompletableFuture — New Methods

orTimeout(): Completes the future exceptionally if it does not complete within the given timeout:

CompletableFuture<String> cf = future.orTimeout(30, TimeUnit.SECONDS);

completeOnTimeout(): Completes the future with a default value if it does not complete within the given timeout:

CompletableFuture<String> cf = future.completeOnTimeout("Default completed value", 30, TimeUnit.SECONDS);

These are some of the most impactful changes across the dozens of differences between Java 8 and Java 11. For the full picture, refer to the JDK 11 Documentation.