Introduction

As Java developers, we will be busy in the upcoming months upgrading to the newest Java releases. It will be a slow, incremental process, especially regarding the migration from classpath to the modulepath. This document tries to summarize new features in a very minimalistic style:

  • focus is on the language level changes, secondarily on deploy/production features

  • use standard Java Class Library as much as possible

  • use JShell as much as possible, a well written introduction to JShell

  • provide links to release notes, JEPs and bug reports

Author

Contributors

  • Karrie Moore

  • Alessandro Sebastiani

  • Matteo Cerina

  • Levin Germann

License

Upgrade to Java 11

Java 11 is the new LTS (Long Term Support), so it is über-important to upgrade as soon as possible to this version.

These are the main features of Java 11:

  • stable java.net.http module

  • TLS 1.3

  • removal of Corba and Java EE (JEP 320)

  • removal of Web Start, with no clear replacement

  • removal of Java applets

  • removal of JavaFX: the FX libraries have moved to the OpenJFX project

  • preparing for removal of Nashorn JavaScript Engine (https://openjdk.java.net/jeps/335)

  • preparing for removal of sun.misc.Unsafe

Which JDK?

However, there are free, zero-cost, alternatives:

Solving migration problems

Be aware that in order to upgrade to Java 11 some non-trivial changes are required, depending by your project:

JEP 320

Several packages, such as JAXB, are not provided by the JDK anymore. It is required to provide an external dependency for them.

For example, to use JAXB in Apache Maven just add:

<dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
        <version>2.3.0</version>
</dependency>
<dependency>
        <groupId>org.glassfish.jaxb</groupId>
        <artifactId>jaxb-runtime</artifactId>
        <version>2.3.0.1</version>
</dependency>

Libraries, frameworks and tools

Launch Single-File Source-Code Programs

Historically to run a Java program we had to:

dfa@aman:~ $ vim Hello.java
dfa@aman:~ $ cat Hello.java (1)
public class Hello { (1)
        public static void main(String[] args) {
                System.out.println("hello world!");
        }
}
dfa@aman:~ $ javac Hello.java (2)
dfa@aman:~ $ java Hello (2)
hello world!
  1. class name and file name must match

  2. two separate steps: compile and run

Now it is possible to edit and run a single-file Java program with much less ceremony:

dfa@aman:~ $ vim demo.java
dfa@aman:~ $ cat demo.java (1)
public class Hello { (1)
        public static void main(String[] args) {
                System.out.println("hello world!");
        }
}
dfa@aman:~ $ java hello.java (2)
hello world!
  1. it is also possible to name file and class differently

  2. one step to compile and run

java.net.http

Introduced in Java 9 and promoted from incubator in Java 11. This is a rather big API and it is a huge step forward from java.net.URLConnection.

Let’s start with an example with httpbin:

dfa@aman:~ $ docker run --rm -p 80:80 kennethreitz/httpbin
[2018-09-29 10:04:20 +0000] [1] [INFO] Starting gunicorn 19.9.0
[2018-09-29 10:04:20 +0000] [1] [INFO] Listening at: http://0.0.0.0:80 (1)
[2018-09-29 10:04:20 +0000] [1] [INFO] Using worker: gevent
[2018-09-29 10:04:20 +0000] [9] [INFO] Booting worker with pid: 9

This container exposes well-known resources, check https://www.kennethreitz.org/essays/httpbin for reference.

Let’s test it with curl:

dfa@aman:~ $ curl localhost/user-agent
{
  "user-agent": "curl/7.54.0"
}

Everything looks fine, now let’s write a simple HTTP2 client using java.net.http module:

import java.net.URI;
import java.net.http.*;
import java.time.Duration;

public class HttpClientDemo {
                public static void main(String[] args) throws Exception {
                                if (args.length != 1) {
                                        System.err.println("missing URL argument");
                                        System.exit(1);
                                }
                                URI uri = URI.create(args[0]);
                                HttpClient client = HttpClient.newBuilder()
                                                .version(HttpClient.Version.HTTP_2)
                                                .build();
                                HttpRequest request = HttpRequest.newBuilder()
                                                .uri(uri)
                                                .timeout(Duration.ofSeconds(1))
                                                .build();
                                HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
                                System.out.print(response.body());
                }
}

and let’s start it using JEP 330:

dfa@aman:~ $ java java11_http_client.java http://localhost/user-agent
{
  "user-agent": "Java-http-client/11"
}

Let’s try simulating a busy web server, that delays each request by 2 seconds (whereas our client timeouts after 1 second):

dfa@aman:~ $ java java11_http_client.java http://localhost/delay/2
Exception in thread "main" java.net.http.HttpTimeoutException: request timed out (1)
	at java.net.http/jdk.internal.net.http.HttpClientImpl.send(HttpClientImpl.java:559)
	at java.net.http/jdk.internal.net.http.HttpClientFacade.send(HttpClientFacade.java:119)
	at HttpClientDemo.main(java11_http_client.java:19)
  1. as expected a timeout is triggered on the client side

WebSocket and WebSocketListener

This module also support web sockets:

var client = HttpClient.newHttpClient();
var uri = URI.create(...);
var listener = ...;
var ws = client.newWebSocketBuilder().buildAsync(uri, listener);

Security

This version includes several new important and modern crypto features:

var in lambda

(var x, var y) -> x.process(y)

is now equivalent to:

(x, y) -> x.process(y)

The primary advantage is that now it is possible to annotate parameters, e.g. for static analysis.

Optional.isEmpty()

In addition of Optional.isPresent now it is possible to use Optional.isEmpty:

Optional<String> featureToggle = ...;
if (featureToggle.isEmpty()) {
        logger.warn("feature 'xxx' disabled");
}

ArrayIndexOutOfBounds

Improved error message with index and current size of the array:

jshell> int[] a = { 1 }
a ==> int[1] { 1 }

jshell> a[4]
|  Exception java.lang.ArrayIndexOutOfBoundsException: Index 4 out of bounds for length 1
|        at (#2:1)

Before this change the message was much more cryptic:

jshell> int[] a = { 1 }
a ==> int[1] { 1 }

jshell> a[4]
|  java.lang.ArrayIndexOutOfBoundsException thrown: 4
|        at (#4:1)

Character.toString(int)

This method returns the string representation for the given Unicode code point as shown below:

String d = Character.toString(100)
assert d.equals("d")

String a = Character.toString(65);
assert a.equals("A")

String.lines()

Create a Stream<String> by lazily splitting string using line separators (e.g. "\n" "\r" "\r\n"):

jshell> "a\nb\nc\n".lines().map(String::toUpperCase).toArray()
$1 ==> Object[2] { "A", "B", "C }

String.repeat()

Repeat String for the specified number of times:

jshell> "ab".repeat(5) (1)
$1 ==> "ababababab"

jshell> "ab".repeat(1) (2)
$2 ==> "ab"

jshell> "ab".repeat(0) (3)
$3 ==> ""

jshell> "ab".repeat(-1) (4)
|  Exception java.lang.IllegalArgumentException: count is negative: -1
|        at String.repeat (String.java:3149)
|        at (#1:1)

jshell> "ab".repeat(Integer.MAX_VALUE) (5)
|  Exception java.lang.OutOfMemoryError: Repeating 4 bytes String 2147483647 times will produce a String exceeding maximum size.
|        at String.repeat (String.java:3164)
|        at (#2:1)
  1. n=5 as expected result is "ab" repeated five times

  2. corner case n=1, result is "ab"

  3. corner case n=0, result is empty ""

  4. error, since n<0

  5. fail fast method in order to avoid allocating a big chunk of memory

String.isBlank()

This is a Unicode-aware alternative to isEmpty():

jshell> var halfSpace = "\u0020"
halfSpace ==> " "

jshell> var fullSpace = "\u3000"
fullSpace ==> " "

jshell> halfSpace.trim().isEmpty()
$1 ==> true (1)

jshell> fullSpace.trim().isEmpty()
$2 ==> false (2)
  1. working as expected

  2. not working as expected

To fix this problem let’s use Character.isWhitespace method, that is aware of the different types of spaces:

boolean blank = string.codePoints().allMatch(Character::isWhitespace);

This is correct but too technical and perhaps the intent is not clear:

As per Java 11 it is possible to just use String.isBlank:

jshell> var halfSpace = "\u0020"
halfSpace ==> " "

jshell> var fullSpace = "\u3000"
fullSpace ==> " "

jshell> halfSpace.repeat(10).isBlank()
$1 ==> true

jshell> fullSpace.repeat(10).isBlank()
$2 ==> true

Be aware that there are surprising results around the definition of Character.isWhitespace, such as non-breaking spaces or newlines:

jshell> var nonBreakingSpace = "\u00A0"
nonBreakingSpace ==> " "

jshell> nonBreakingSpace.isBlank()
$1 ==> false

String.strip()

Whilst almost the same as trim()/trimLeft()/trimRight(), this takes full-width spaces as a space (0x20 ASCII character).

jshell> var halfSpace = "\u0020"
halfSpace ==> " "

jshell> halfSpace.trim()
$2 ==> ""

jshell> var fullSpace = "\u3000"
$3 ==> " "

jshell> fullSpace.trim()
$4 ==> " " (1)

jshell> fullSpace.strip()
$5 ==> "" (2)
  1. not working as expected

  2. working as expected

Finally let’s cover quickly stripLeading()/stripTrailing():

jshell> var text = fullSpace + "foo bar" + fullSpace
text ==> " foo bar "

jshell> text.stripTrailing()
$7 ==> " foo bar"

jshell> text.stripLeading()
$8 ==> "foo bar "

CharSequence.compare()

Using this new API it is possible to compare any CharSequence implementation:

jshell> var builder = new StringBuilder("aaa");
builder ==> aaa

jshell> var buffer = new StringBuffer("aaa");
buffer ==> aaa

jshell> var string = "aaa";
string ==> "aaa"

jshell> CharSequence.compare(builder, buffer); (1)
$1 ==> 0

jshell> CharSequence.compare(string, buffer); (2)
$2 ==> 0
  1. comparing a StringBuilder with StringBuffer yields 0

  2. ditto for comparing a String with StringBuffer

Null objects for Reader/Writer and InputStream/OutputStream

  • Reader.nullReader()

  • Writer.nullWriter()

  • InputStream.nullInputStream()

  • OutputStream.nullOutputStream()

These objects are very useful during unit testing.

Additions to java.nio.file.Files

  • String readString(Path, Charset): reads all content from a file into a string, decoding from bytes to characters using the specified charset;

  • Path writeString(Path, CharSequence, Charset, OpenOption[]): writes a CharSequence (e.g. String, StringBuilder) to a file. Characters are encoded into bytes using the specified charset.

It is very convenient to use java.nio.charset.StandardCharsets (introduced in Java 7, see javadoc):

String content = Files.readString(Path.of("main.adoc"), StandardCharsets.UTF_8);

java.nio.Path.of()

This is a very nice shortcut to build paths:

jshell> Path.of("dir", "subdir", "file")
$2 ==> dir/subdir/file

Returns a Path by converting a path string, or a sequence of strings that when joined form a path string. Please note that this API is following the Item 42 of Effective Java:

jshell> Path.of()
|  Error:
|  no suitable method found for of(no arguments)
|      method java.nio.file.Path.of(java.lang.String,java.lang.String...) is not applicable
|        (actual and formal argument lists differ in length)
|      method java.nio.file.Path.of(java.net.URI) is not applicable
|        (actual and formal argument lists differ in length)
|  Path.of()
|  ^-----^

because the definition is:

public static Path of(String first, String... more) {
    ...
}

Unicode

This release includes combined support for both Unicode 9.0 as well as Unicode 10.0.

By now it is possible to use the Bitcoin sign (code point U+20BF) released on June 2017 as part of Unicode 10.0.

Dynamic Class-File Constants

This is mostly for compilers that target the JVM. However it could have interesting ripples in the whole ecosystem, as invokedynamic did.

Java Mission Control & Flight Recorder

Java Flight Recorder and Java Mission Control together create a complete tool chain to continuously collect low level and detailed runtime information enabling after-the-fact incident analysis. Development website https://wiki.openjdk.java.net/display/jmc/Main

Upgrade to Java 10

Main features:

  • JEP 286 local type inference

  • JEP 304 garbage collector interface

  • JEP 317 experimental java based JIT compiler

  • JEP 307 Parallel full gc for G1

  • JEP 310 application class-data sharing

  • JEP 312 thread-local handshakes

  • JEP 313 javah removal

  • JEP 314 unicode extensions

  • JEP 319 root certificates, to easy migrate from Oracle JDK → OpenJDK

  • removal of policytool

Local type inference

This is the big new feature of Java 10.

HashMap<String, String> a = new HashMap<>();
var b = new HashMap<String, String>();
assert a.equals(b);

Often using var is quite convenient:

Map<String, Integer> map = Map.of("a", 1, "b", 2); (1)
for (Map.Entry<String, Integer> entry : map.entrySet()) { (1)
    System.out.println(entry);
}

var map2 = Map.copyOf(map); (2)
for (var entry : map2.entrySet()) { (2)
    System.out.println(entry);
}
  1. Map<String, Integer> is spread all over

  2. much more easy to read

It is important to know that var is not a keyword, it is a special type: in this way you can continue to use var as variable name or method name (see section "3.9" of The Java Language Specification, Java SE 11 Edition):

jshell> var var = 1 (1)
var ==> 1

jshell> class var { } (2)
|  Error:
|  'var' not allowed here
|    as of release 10, 'var' is a restricted local variable type and cannot be used for type declarations
|  class var { }
  1. still possible to use var as variable name

  2. not possible anymore to use var as class or interface

Note
now it is possible to use anonymous types, this was not possible before:
var a = new Object() {
   void m() {
   }
};
a.m(); (1)
  1. the type of a is Object + method m()

By using var wisely it could be possible to make easier to perform large scale refactorings, but this idea must be proven. Don’t miss the Style Guidelines for Local Variable Type Inference in Java.

Docker awareness

JVM now can detect CPU/memory settings when run inside a container. Given a docker setup with 4 CPUs and 2GB of memory:

dfa@aman ~ $ docker container run -it --rm --cpus 1 openjdk:9-jdk-slim (1)
Sep 29, 2018 7:56:31 AM java.util.prefs.FileSystemPreferences$1 run
INFO: Created user preferences directory.
|  Welcome to JShell -- Version 9.0.4
|  For an introduction type: /help intro

jshell> Runtime.getRuntime().availableProcessors()
$1 ==> 4 (2)
  1. 1 CPU requested

  2. 4 CPU available

Before this release JVM was not aware of this --cpus 1, so this is why JVM sees 4 CPUs. Whereas in Java 10:

dfa@aman:~ $ docker container run -it --rm --cpus 1 openjdk:10-jdk-slim (1)
Sep 29, 2018 8:11:29 AM java.util.prefs.FileSystemPreferences$1 run
INFO: Created user preferences directory.
|  Welcome to JShell -- Version 10.0.2
|  For an introduction type: /help intro

jshell> Runtime.getRuntime().availableProcessors()
$1 ==> 1 (2)
  1. 1 CPU requested

  2. 1 CPU available

It is possible to use also a cpu-set:

dfa@aman:~ $ docker container run -it --rm --cpuset-cpus="1,2"  openjdk:10-jdk-slim
Sep 29, 2018 8:14:53 AM java.util.prefs.FileSystemPreferences$1 run
INFO: Created user preferences directory.
|  Welcome to JShell -- Version 10.0.2
|  For an introduction type: /help intro

jshell> Runtime.getRuntime().availableProcessors()
$1 ==> 2

Regarding memory setting, by default JVM uses 1/4 of the memory, 2 GB in the following example:

dfa@aman:~ $ docker container run -it --rm openjdk:10-jdk-slim
Sep 29, 2018 8:20:47 AM java.util.prefs.FileSystemPreferences$1 run
INFO: Created user preferences directory.
|  Welcome to JShell -- Version 10.0.2
|  For an introduction type: /help intro

jshell> Runtime.getRuntime().maxMemory() / 1024 / 1024
$2 ==> 500 (1)
  1. the JVM sees 500MB

Without constraints JVM is going to use 1/4 of the available memory to docker, 500MB.

dfa@aman:~ $ docker container run -it --rm --memory 512M openjdk:10-jdk-slim (1)
Sep 29, 2018 8:25:12 AM java.util.prefs.FileSystemPreferences$1 run
INFO: Created user preferences directory.
|  Welcome to JShell -- Version 10.0.2
|  For an introduction type: /help intro

jshell> Runtime.getRuntime().maxMemory() / 1024 / 1024
$1 ==> 123 (2)
  1. requesting a constraint of memory

  2. the JVM sees 123MB

It is possible then to fine tune the memory settings by using JVM flags -Xmx, -Xms, etc. A very nice explanation about Docker and JVM memory settings can be found here https://stackoverflow.com/questions/53451103/

References

More on this can be found in the following tickets:

new javadoc @summary tag

In order to be precise and avoid ambiguities around the special handling of the first sentence of javadoc, it is possible to use @summary:

{@summary This is the first sentence.} This is the second sentence.

java.io.Reader.transferTo(Writer)

A long awaited feature, usually provided by external libraries such as Apache IOUtils:

jshell> import java.io.*

jshell> var a = new StringReader("hello world")
a ==> java.io.StringReader@3abbfa04

jshell> var b = new StringWriter()
b ==>

jshell> a.transferTo(b)
$4 ==> 11

jshell> b
b ==> hello world

RuntimeMXBean.getPid()

jshell> import java.lang.management.*

jshell> ProcessHandle.current()
$1 ==> 20674 (2)

jshell> ManagementFactory.getRuntimeMXBean().getPid()
$2 ==> 11429 (1)
  1. current PID is exported also via JMX

Upgrade to Java 9

Java 9 delivers an impressive set of features. By far, the most important are:

Describing any of these items is beyond the scope of this document.

Note
It is important to say that you don’t need modules to run on Java 9, classpath is still supported.

Immutable collections

JEP 269 introduced some new factory methods for collections:

Set<Integer> set = Set.of(1,2,3,4);
List<Integer> list = List.of(1,2,1,2);
Map<String, Integer> map = Map.of("key1", 1, "key2", 2);
Map<String, Integer> mapLonger = Map.ofEntries(Map.entry("key1", 1), Map.entry("key2", 2));
Note
Immutability vs views
Collections.unmodifiable are just read-only wrappers around original data structure: if the original data structure is mutable, you can still see changes in the wrapper.
jshell> List<Object> list = new ArrayList<>()
list ==> []

jshell> List<Object> unmodifiableList = Collections.unmodifiableList(list)
unmodifiableList ==> []

jshell> list.add(1) (1)
$3 ==> true

jshell> unmodifiableList (1)
unmodifiableList ==> [1]
  1. changes to unmodifiableList are still possible

By using these new methods you get immutable data structures:

jshell> List<Object> immutableList = List.of();
immutableList ==> []

jshell> immutableList.add(1)
|  Exception java.lang.UnsupportedOperationException
|        at ImmutableCollections.uoe (ImmutableCollections.java:71)
|        at ImmutableCollections$AbstractImmutableCollection.add (ImmutableCollections.java:75)
|        at (#6:1)
Note
sometimes List.copyOf is a no-op
jshell> List<Integer> a = List.of(1,2,3)
a ==> [1, 2, 3]

jshell> List<Integer> b = List.copyOf(a)
b ==> [1, 2, 3]

jshell> a == b
$7 ==> true
Note
it is not possible to use Set.of() to filter away duplicates
jshell> Set.of(1,1)
|  java.lang.IllegalArgumentException thrown: duplicate element: 1
|        at ImmutableCollections$SetN.<init> (ImmutableCollections.java:463)
|        at Set.of (Set.java:521)

Unmodifiable collectors

Now it is possible to use java.util.stream.Collectors to build unmodifiable collections, avoiding using Collections factory methods.

For example:

jshell> Collections.unmodifiableList(Stream.of(1,2,3).collect(Collectors.toList()))
$1 ==> [1, 2, 3]

jshell> Stream.of(1,2,3).collect(Collectors.toUnmodifiableList())
$2 ==> [1, 2, 3]

Likewise it is possible to build immutable lists and maps:

jshell> Stream.of(1,2,3).collect(Collectors.toUnmodifiableList())
$26 ==> [1, 2, 3]

jshell> Stream.of(1,2,3).collect(Collectors.toUnmodifiableMap(e -> e, e -> e * e))
$27 ==> {1=1, 2=4, 3=9}

java.util.Optional.stream()

This is a small enhancement that enables the use of Stream.flatMap(). For example:

jshell> List<Optional<Integer>> data = List.of(Optional.of(42), Optional.empty(), Optional.of(-1));
data ==> [Optional[42], Optional.empty, Optional[-1]]

jshell> data.stream().filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList()); (1)
$1 ==> [42, -1]

jshell> data.stream().flatMap(Optional::stream).collect(Collectors.toList()); (2)
$2 ==> [42, -1]
List<Optional<String>> listOfOptionals = something();
  1. this is a common Java 8 pattern: filter then map

  2. this is Java 9

Please note that Optional.stream() implementation is straightforward (source has been taken from JDK9):

public Stream<T> stream() {
    if (!isPresent()) {
        return Stream.empty();
    } else {
        return Stream.of(value);
    }
}

Stream.takeWhile()/Stream.dropWhile()

Another nice addiction to the Stream API is takeWhile and dropWhile:

IntStream
  .iterate(1, n -> n + 1)
  .takeWhile(n -> n < 10)
  .forEach(System.out::println);

More concurrency updates

Stack walk API

StackWalker instances are thread-safe and thus can be shared between threads: each thread will see its own stack. Additionally a security check is performed on creation of the StackWalker instance, no further checks are performed later.

This is useful in several occasions and now it is possible to capture a partial stacktrace in a very simple and effective way.

That could be used to write a generic LoggerFactory for JUL:

import java.lang.StackWalker.Option;
import java.util.logging.Logger;

public class LoggerFactory {
        public static Logger forEnclosingClass() {
                Class<?> callerClass = StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE).getCallerClass();
                return Logger.getLogger(callerClass.getCanonicalName());
        }
}

// client code
public class Demo {

    private final Logger logger = LoggerFactory.forEnclosingClass();

}

Milling Project Coin

The small language changes included in Project Coin were low hanging fruits but nevertheless the project was quite successful. Java 9 introduces the following small changes:

Let’s quickly dig the last point since it is a trivial source-level incompatibility to fix:

jshell> Predicate<Integer> pred = _ -> false;
|  Error:
|  '_' used as an identifier
|    (use of '_' as an identifier is forbidden for lambda parameters)
|  Predicate<Integer> pred = _ -> false;
|                            ^

java.lang.Runtime.Version

In legacy code bases it is possible to find various ways to determine which java version is running:

String version = Runtime.class.getPackage().getImplementationVersion();
double version = Double.parseDouble(System.getProperty("java.specification.version"));
String[] javaVersionElements = System.getProperty("java.runtime.version").split("\\.|_|-b");

Now we have a nice standard API to do that:

Runtime.Version version = Runtime.version();

Version it’s a value object and it is Comparable<Version>. It is even possible to create a Version instance using an externally provided version string:

import java.lang.Runtime.Version;
import java.lang.System;

String versionString = System.getProperty("java.version");
Version version = Version.parse(versionString);
System.out.printf("java %d.%d%n", version.major(), version.minor());

java.lang.ProcessHandle

Obtain information about JVM itself:

ProcessHandle self = ProcessHandle.current();
ProcessHandle.Info procInfo = self.info();
System.out.println(procInfo);

List all system processes:

ProcessHandle.allProcesses().map(ProcessHandle::info).forEach(System.out::println);

Sample output on macOS:

[user: Optional[dfa], cmd: /Library/Java/JavaVirtualMachines/jdk-11.jdk/Contents/Home/bin/java, args: [-agentlib:jdwp=transport=dt_socket,address=localhost:61581, jdk.jshell.execution.RemoteExecutionControl, 61580], startTime: Optional[2018-10-19T06:15:44.622Z], totalTime: Optional[PT0.459124S]]
[user: Optional[dfa], cmd: /Library/Java/Home//bin/jshell, startTime: Optional[2018-10-19T06:15:44.225Z]]
[user: Optional[root], startTime: Optional[2018-10-19T06:14:33.378Z]]
[user: Optional[dfa], cmd: /System/Library/PrivateFrameworks/AOSKit.framework/Versions/A/XPCServices/com.apple.iCloudHelper.xpc/Contents/MacOS/com.apple.iCloudHelper, startTime: Optional[2018-10-19T06:14:33.081Z]]
[user: Optional[dfa], cmd: /Library/Java/JavaVirtualMachines/jdk-11.jdk/Contents/Home/bin/java, args: [-agentlib:jdwp=transport=dt_socket,address=localhost:61572, jdk.jshell.execution.RemoteExecutionControl, 61571], startTime: Optional[2018-10-19T06:14:32.070Z]]
...

@Deprecated enhancements

It is possible to mark a method/class for removal (forRemoval) and when the deprecation started (since):

@Deprecate(forRemoval=true)
public void foo() {

}

@Deprecate(since="1.0")
public void bar() {

}

Unified JVM logging

This is invaluable for debugging production problems.

For example, it is possible to include every tag around gc:

$ java -Xlog:gc* -jar myapp.jar
[0.012s][info][gc,heap] Heap region size: 1M
[0.018s][info][gc     ] Using G1
[0.019s][info][gc,heap,coops] Heap address: 0x00000006c0000000, size: 4096 MB, Compressed Oops mode: Zero based, Oop shift amount: 3
... application output
... exit
[1.803s][info][gc,heap,exit ] Heap
[1.803s][info][gc,heap,exit ]  garbage-first heap   total 262144K, used 11264K [0x00000006c0000000, 0x00000007c0000000)
[1.803s][info][gc,heap,exit ]   region size 1024K, 12 young (12288K), 0 survivors (0K)
[1.803s][info][gc,heap,exit ]  Metaspace       used 10805K, capacity 11186K, committed 11520K, reserved 1058816K
[1.803s][info][gc,heap,exit ]   class space    used 1042K, capacity 1203K, committed 1280K, reserved 1048576K

Or restrict to a more specific tag, such as gc+heap:

$ java -Xlog:gc,gc+heap -jar myapp.jar
[0.013s][info][gc,heap] Heap region size: 1M
[0.019s][info][gc     ] Using G1
... application output
[89.471s][info][gc,heap] GC(0) Eden regions: 24->0(151)
[89.471s][info][gc,heap] GC(0) Survivor regions: 0->2(3)
[89.471s][info][gc,heap] GC(0) Old regions: 0->0
[89.471s][info][gc,heap] GC(0) Humongous regions: 0->0
[89.471s][info][gc     ] GC(0) Pause Young (G1 Evacuation Pause) 24M->1M(256M) 3.926ms
... exit

Compact Strings

This is just a more efficient internal representation of java.lang.String, no public methods have been changed.

In practice, the internal representation of string has been changed from char[] to byte[] + flag. The purpose of the flag is to store which encoding to use:

  • ISO-8859-1/Latin-1 (one byte per character)

  • UTF-16 (two bytes per character).

Appendix: Upgrade to Java 8

The main features of Java 8 are:

Nevertheless there are hidden gems in this release, that often are ignored.

java.io.UncheckedIOException

This is a little known class that wraps an IOException with an unchecked exception.

StampedLocks

A fast alternative to ReadWriteLock, that has an optimistic mode.

Concurrent Adders

A LongAdder could be a great alternative to AtomicLong for high contention use cases.

Strong algorithm for SecureRandom

A new API has been added:

public static SecureRandom getInstanceStrong()
                                      throws NoSuchAlgorithmException

Overflow free operations

A great addition is a set of new methods to perform basic math operation, throwing exceptions when overflow are detected:

jshell> Integer.MAX_VALUE * 2 (1)
$1 ==> -2
jshell> Math.multiplyExact(Integer.MAX_VALUE, 2) (2)
|  Exception java.lang.ArithmeticException: integer overflow
|        at Math.multiplyExact (Math.java:906)
|        at (#2:1)
  1. silent error: the result is -2, that is spectacularly wrong

  2. loud error

String.join()

This is a static method on java.lang.String:

jshell> String.join(",");
$10 ==> ""

jshell> String.join(",", "a");
$11 ==> "a"

jshell> String.join(",", "a", "b");
$12 ==> "a,b"

There are two overloads of this method: one for String…​ and another one for Iterable<? extends CharSequence>.

Generalized Target-Type Inference

This is a little know feature, almost invisible but it is extremely valuable for users of our classes:

jshell> void foo(List<String> args) { System.out.println(args); }
|  created method foo(List<String>)

jshell> foo(Collections.<String>emptyList()) (1)
[]

jshell> foo(Collections.emptyList()) (2)
[a, b]
  1. Java 7: <String> is needed by the compiler to avoid this error incompatible types: java.util.List<java.lang.Object> cannot be converted to java.util.List<java.lang.String>

  2. Java 8: <String> is not needed anymore: compiler can infers it

Explicit Receiver Parameters

I discovered this recently but never found a decent use of this feature:

jshell> public class Foo {
   ...>   public void foo() { }
   ...> }
|  created class Foo (1)

jshell> public class Foo2 {
   ...>   public void foo(Foo2 this) { }
   ...> }
|  created class Foo2 (2)
  1. without explicit receiver type

  2. with explicit receiver type, fully equivalent to <1>

The purpose is to allow receiver type to be annotated, later it is possible to make use of the annotation by using Executable#getAnnotatedReceiverType. Please note that java.lang.reflect.Executable is the superclass of both Method as well as Constructor.

Arrays

Very specific yet useful methods has been added to java.util.Arrays:

  • parallelSort

  • parallelPrefix

  • parallelSetAll

  • setAll

Warning
be aware that parallelPrefix with double[] may yield different results of a sequential algorithm (this because floating point operation may be not associative).

It is very easy to implement Triangular number with this new methods. Let’s explore the solution with JShell:

jshell> int[] array = new int[10]
array ==> int[10] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }

jshell> Arrays.setAll(array, idx -> idx + 1)

jshell> array
array ==> int[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }

jshell> Arrays.parallelPrefix(array, (acc, e) -> acc + e)

jshell> array
array ==> int[10] { 1, 3, 6, 10, 15, 21, 28, 36, 45, 55 }

Let’s put all the pieces together:

jshell> int[] triangularNumber(int n) {
   ...>   int[] result = new int[n];
   ...>   Arrays.parallelSetAll(result, idx -> idx + 1);
   ...>   Arrays.parallelPrefix(result, (acc, e) -> acc + e);
   ...>   return result;
   ...> }
|  created method triangularNumber(int)

jshell> triangularNumber(4)
$1 ==> int[4] { 1, 3, 6, 10 }

jshell> triangularNumber(10)
$2 ==> int[10] { 1, 3, 6, 10, 15, 21, 28, 36, 45, 55 }

jshell> triangularNumber(100)
$3 ==> int[100] { 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78, 91, 105, 120, 136, 153, 171, 190, 210, 231, 253, 276, 300, 325, 351, 378, 406, 435, 465, 496, 528, 561, 595, 630, 666, 703, 741, 780, 820, 861, 903, 946, 990, 1035, 1081, 1128, 1176, 1225, 1275, 1326, 1378, 1431, 1485, 1540, 1596, 1653, 1711, 1770, 1830, 1891, 1953, 2016, 2080, 2145, 2211, 2278, 2346, 2415, 2485, 2556, 2628, 2701, 2775, 2850, 2926, 3003, 3081, 3160, 3240, 3321, 3403, 3486, 3570, 3655, 3741, 3828, 3916, 4005, 4095, 4186, 4278, 4371, 4465, 4560, 4656, 4753, 4851, 4950, 5050 }