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
-
Davide Angelocola https://dfa1.github.io
Contributors
-
Karrie Moore
-
Alessandro Sebastiani
-
Matteo Cerina
-
Levin Germann
License
This document is licensed under a Creative Commons Attribution-ShareAlike 4.0 International 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?
Since JDK11 is a commercial product, to use it in production you have to pay Oracle, details here: https://www.oracle.com/technetwork/java/javaseproducts/overview/javasesubscriptionfaq-4891443.html.
However, there are free, zero-cost, alternatives:
-
OpenJDK binaries with related docker images
-
adoptopenjdk project, that provides pre-built JDK binaries as well as docker images. More details can be found https://blog.joda.org/2018/09/time-to-look-beyond-oracles-jdk.html
-
Amazon Corretto, another free OpenJDK distribution maintained by Amazon
Solving migration problems
Be aware that in order to upgrade to Java 11 some non-trivial changes are required, depending by your project:
-
removal of Java EE and Corba JEP 320: non trivial, see below
-
removal of
Thread.destroy()
andThread.stop(Throwable)
methods, see https://bugs.openjdk.java.net/browse/JDK-8204243: trivial change -
removal of
com.sun.awt.AWTUtilities
class https://bugs.openjdk.java.net/browse/JDK-8200149: this should be a mechanical change -
removal of
sun.misc.Unsafe.defineClass
https://bugs.openjdk.java.net/browse/JDK-8193033 users should use the public replacement,java.lang.invoke.MethodHandles.Lookup.defineClass
, added in Java 9 -
source incompatibility for
java.util.Stream.toArray(null)
, see https://bugs.openjdk.java.net/browse/JDK-8060192
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>
This is a very good reference: https://stackoverflow.com/questions/48204141/replacements-for-deprecated-jpms-modules-with-java-ee-apis/48204154#48204154
Libraries, frameworks and tools
-
upgrade to ASM 7.0 for Java 11 support https://asm.ow2.io/versions.html
-
upgrade to Spring 5.1 https://jira.spring.io/browse/SPR-16391
-
upgrade to Hibernate 5.3.3+ http://in.relation.to/2018/09/13/using-hibernate-orm-with-jdk11/
-
upgrade to Apache Maven plugins to latest version (e.g. maven-compiler-plugin to 3.8.0)
-
gradle TBD
-
sonarcube fully supports Java 11 (at least for me using version 7.4 with SonarJava 5.10, probably also earlier versions would be fine)
Launch Single-File Source-Code Programs
Historically to run a Java program we had to:
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!
-
class name and file name must match
-
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:~ $ 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!
-
it is also possible to name file and class differently
-
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)
-
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:
-
TLS 1.3, see https://bugs.openjdk.java.net/browse/JDK-8202625
-
ChaCha20 & Poly1305 crypto algorithms
-
Key Agreement with Curve25519 and Curve448
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)
-
n=5 as expected result is
"ab"
repeated five times -
corner case
n=1
, result is"ab"
-
corner case
n=0
, result is empty""
-
error, since
n<0
-
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)
-
working as expected
-
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)
-
not working as expected
-
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
-
comparing a
StringBuilder
withStringBuffer
yields 0 -
ditto for comparing a
String
withStringBuffer
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 aCharSequence
(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);
}
-
Map<String, Integer>
is spread all over -
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 { }
-
still possible to use
var
as variable name -
not possible anymore to use
var
asclass
orinterface
Note
|
now it is possible to use anonymous types, this was not possible before: |
var a = new Object() {
void m() {
}
};
a.m(); (1)
-
the type of
a
isObject
+ methodm()
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 CPU requested
-
4 CPUs 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 CPU requested
-
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, 2GB 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)
-
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)
-
requesting a constraint of memory
-
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 topic can be found in the following tickets:
-
improve docker container detection and resource configuration usage https://bugs.openjdk.java.net/browse/JDK-8146115
-
allow more flexibility in selecting Heap % of available RAM https://bugs.openjdk.java.net/browse/JDK-8186248
-
jcmd attach in linux should be relative to /proc/pid/root and namespace aware https://bugs.openjdk.java.net/browse/JDK-8179498
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 (1)
jshell> ManagementFactory.getRuntimeMXBean().getPid()
$2 ==> 11429 (2)
-
current JVM PID
-
and exported via JMX
Upgrade to Java 9
Java 9 delivers an impressive set of features. By far, the most important are:
-
Project Jigsaw aka modules https://openjdk.java.net/projects/jigsaw/
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]
-
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();
-
this is a common Java 8 pattern:
filter
thenmap
-
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
-
Reactive Streams publish-subscribe framework (see http://www.reactive-streams.org/)
-
refinements in
java.util.concurrent.CompletableFuture
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:
-
@SafeVarargs
on private methods, https://bugs.openjdk.java.net/browse/JDK-7196160 -
private
methods in interfaces -
allow final or effectively final variables to be used as resources in try-with-resources https://bugs.openjdk.java.net/browse/JDK-7196163
-
_
identifier now is reserved
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:
-
Project Lambda (lambda and streams) https://openjdk.java.net/projects/lambda/
-
ThreeTen (new date/time library) https://openjdk.java.net/projects/threeten/
Nevertheless there are few hidden gems 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)
-
silent error: the result is -2, that is spectacularly wrong
-
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]
-
Java 7:
<String>
is needed by the compiler to avoid this errorincompatible types: java.util.List<java.lang.Object> cannot be converted to java.util.List<java.lang.String>
-
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)
-
without explicit receiver type
-
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 }