What's new in Java 19
Last updated on November 14, 2022 am
Java 19 was released on September 20, 2022. You can download it here .
The most exciting innovation for me are the virtual threads , which have been developed as part of Project Loom for several years and are now finally included in the JDK as a preview.
Virtual threads are a requirement for Structured Concurrency , another exciting new Incubator feature in Java 19.
There is also good news for anyone who wants to access non-Java code (e.g. the C standard library): The Foreign Function & Memory API has now also reached the preview stage after five incubator rounds .
As always, I use the English names of the JEPs.
New System Properties for System.out and System.err
There’s one change that every Java developer should know about that isn’t found in the Java 19 Feature Announcements , but rather buried deep in the release notes .
If you run an existing application with Java 19, it can happen that you only see question marks on the console instead of umlauts and other special characters.
This is due to the fact that from Java 19 the standard encoding of the operating system is used for the output - under Windows System.out
e.g. System.err
“Cp1252”. To switch the output to UTF-8, you need to add the following VM options when calling the application:
-Dstdout.encoding=utf8 -Dstderr.encoding=utf8
If you don’t want to do this every time you start the program, you can also set these settings globally by defining the following environment variable (yes, it starts with an underscore):
_JAVA_OPTIONS="-Dstdout.encoding=utf8 -Dstderr.encoding=utf8"
New Methods to Create Preallocated HashMaps
Traditionally, if we want to create ArrayList
a number of elements that is known in advance (e.g. 120), then we can do it like this:
1 |
|
As a result, the ArrayList
underlying array is allocated directly for 120 elements and does not have to be enlarged several times (i.e. newly created and copied) in order to insert the 120 elements.
Likewise, since time immemorial, we can HashMap
generate one like this:
1 |
|
Intuitively one would think that there is HashMap
room for 120 mappings.
However, this is not the case!
Because HashMap
it is initialized with a default load factor of 0.75. This means: As soon HashMap
as it is 75% full, it will be rebuilt with twice the size (“rehashed”). This is to ensure that the elements HashMap
are distributed as evenly as possible across the buckets and that no bucket contains more than one element.
So the one initialized with a capacity of 120 HashMap
can only hold 120 × 0.75 = 90 mappings.
Previously, to create one HashMap
for 120 mappings, you had to calculate the capacity yourself by dividing the number of mappings by the load factor: 120 ÷ 0.75 = 160.
So one HashMap
for 120 mappings had to be created as follows:
1 |
|
Java 19 makes this easier for us - we can now write the following instead:
1 |
|
If we look at the source code of the new methods, we see that the end result is the same as what we did manually before:
1 |
|
The newHashMap()
method has also been built into LinkedHashMap
and WeakHashMap
.
There is no JDK Enhancement Proposal for this extension.
Preview and Incubator features
Java 19 gives us a total of six preview and incubator features, i.e. features that are not yet finished but can already be tested by the developer community. Community feedback is typically incorporated into the further development and completion of these features.
Pattern Matching for switch (Third Preview)
Let’s start with a feature that has already had two preview rounds. The “pattern matching for switch” first introduced in Java 17 allowed us to write code like the following:
1 |
|
We can use it to switch
check within a statement whether an object is of a certain class and whether it has other properties (as in the example: longer than 5 characters).
In Java 19, the syntax of the so-called “Guarded Pattern” (in the example above “ “) was changed with JDK Enhancement Proposal 427 . The new keyword must now be used String s && s.length() > 5
instead .&&``when
The above example is notated as follows in Java 19:
1 |
|
when``case
is a so-called “contextual keyword” and therefore only has a meaning within a label. If you have variables or methods named “when” in your code, you don’t need to change anything.
Record Patterns (Preview)
Staying with the topic “Pattern Matching” we come to the “Record Patterns”. If you are new to Records, I recommend reading the Records in Java article first .
The best way to explain what a record pattern is is with an example. Let’s assume we have defined the following record:
1 |
|
We also have a print()
method that can return any object, including positions:
1 |
|
In case you stumble over the notation used - it was introduced in Java 16 as “Pattern Matching for instanceof”.
Record pattern with instanceof
As of Java 19, JDK Enhancement Proposal 405 allows us to use a so-called “Record Pattern”. With that, we can also write the code as follows:
1 |
|
Instead Position position
of matching “ “ and accessing in the code position
below, we now match “ Position(int x, int y)
“ and can access and directly in the code x
below y
.
Record pattern with switch
Since Java 17 , we can also switch
write the original example as a statement:
1 |
|
Since Java 19 we can also use a record pattern in the switch statement:
1 |
|
Nested record patterns
It is also possible to match nested records - I would also like to demonstrate this with an example.
We first define a second record, Path
, with a start and a target position:
1 |
|
Our print()
method can now easily output all X and Y coordinates of the path using a record pattern:
1 |
|
Alternatively, we can also switch
write this as a statement:
1 |
|
Record patterns offer us an elegant way of accessing the elements of a record after a type check.
Virtual Threads (Preview)
For me, the most exciting innovation in Java 19 are “Virtual Threads”. These have been developed in Project Loom for several years and so far could only be tested with a self-compiled JDK.
With JDK Enhancement Proposal 425 , virtual threads are finally making their way into the official JDK - directly in preview status, so that no major changes to the API are to be expected.
Why we need virtual threads, what they are, how they work, and how to use them is a must-read in the main article on virtual threads .
Foreign Function & Memory API (Preview)
Project Panama has been working on a replacement for the cumbersome, error-prone and slow Java Native Interface (JNI) for a long time.
The “Foreign Memory Access API” and the “Foreign Linker API” were already introduced in Java 14 and Java 16 - both individually at the incubator stage. In Java 17 , these APIs were merged into the “Foreign Function & Memory API” (FFM API), which remained in the incubator stage until Java 18 .
In Java 19, the new API with JDK Enhancement Proposal 424 has finally reached the preview stage, which means that only small changes and bug fixes will be made. So it’s time to introduce the new API here!
The Foreign Function & Memory API enables access to native memory (i.e. memory outside of the Java heap) and access to native code (e.g. C libraries) directly from Java.
I’ll show you how this works with an example. However, I won’t delve too deeply into the topic here, since most Java developers rarely if ever need to access native memory and code.
Here’s a simple example that stores a string in off-heap memory and calls the C Standard Library’s “strlen” function on it:
1 |
|
The one in line 9 is interesting FunctionDescriptor
: it defines the return type of the function as the first parameter and the arguments of the function as additional parameters. This FunctionDescriptor
ensures that all Java types are properly converted to C types and vice versa.
Since the FFM API is still in the preview state, a few additional parameters must be specified to compile and start it:
1 |
|
Anyone who has worked with JNI before - and remembers how much Java and C boilerplate code it took to write and keep in sync - will see that the overhead of calling the native function has been reduced by orders of magnitude.
If you want to delve deeper into the subject: in the JEP you will find further, more complex examples.
Structured Concurrency (Incubator)
If a task consists of different subtasks that can be completed in parallel (e.g. accessing data from a database, calling a remote API and loading a file), we were previously able to use the Java Executur Framework for this.
That could then e.g. look like this:
1 |
|
We pass the three subtasks to the executor and wait for the subresults. The Happy Path is implemented quickly. But how do we handle exceptions?
- If one subtask fails, how can we kill the others?
- How can we cancel the subtasks when the entire calculation is no longer needed?
Both are possible, but require fairly complex, hard-to-maintain code.
And what if we want to debug code of this kind? A thread dump e.g. B. would give us tons of threads named “pool-X-thread-Y” - but we wouldn’t know which pool thread belongs to which calling thread, since all calling threads share the executor’s thread pool.
JDK Enhancement Proposal 428 introduces an API for so-called “structured concurrency”, a concept intended to improve the implementation, readability and maintainability of code for requirements of this type.
Using a StructuredTaskScope
we can rewrite the example as of Java 19 as follows:
1 |
|
So we replace the one in the scope of the class ExecutorService
with one in the scope of the method StructuredTaskScope
– and executor.submit()
with scope.fork()
.
With scope.join()
we wait for all tasks to be completed - or at least one has failed or been canceled. In the latter two cases, the subsequent throws throwIfFailed()
a ExecutionException
or a CancellationException
.
The new approach brings the following improvements over the old one:
- Task and subtasks form a self-contained unit in the code - there is no
ExecutorService
one in a higher scope. The threads don’t come from a thread pool; instead, each subtask runs in a new virtual thread . - As soon as an error occurs in one of the subtasks, all other subtasks are aborted.
- If the calling thread is aborted, the subtasks are also aborted.
- The thread dump shows the call hierarchy between the calling thread and the threads that execute the subtasks.
If you want to try the example yourself: Preview features must be explicitly activated and Incubator modules must be explicitly added to the module path. For example, if you saved the code in a file named StructuredConcurrencyTest.java
, you can compile and run it like this:
1 |
|
Please note that Incubator features are still subject to major changes.
Vector API (Fourth Incubator)
The new Vector API has nothing java.util.Vector
to do with the class. In fact, it’s about a new API for mathematical vector calculations and their mapping to modern SIMD (single-instruction-multiple-data) CPUs.
The Vector API has been part of the JDK as an incubator since Java 16 and was further developed in Java 17 and Java 18 .
With JDK Enhancement Proposal 426 , Java 19 delivers the fourth iteration in which the API has been extended to include new vector operations - as well as the ability to store vectors in memory segments (a feature of the Foreign Function & Memory API ) and read them from them.
Incubator features are still subject to significant changes, so I won’t detail the API here. I’ll catch up on that once the Vector API has transitioned to preview status.
Deprecations and Deletion
In Java 19, some features were marked as “deprecated” or deprecated.
Deprecation of Locale class constructors
In Java 19, the public constructors of the Locale
class have been marked as “deprecated”.
Instead, we should use the new static factory method Locale.of()
. This ensures that there is Locale
only one instance per configuration.
The following example shows the usage of the factory method versus the constructor:
1 |
|
If you start this code, you will see that the objects returned via the factory method are identical to the Locale
constants - the ones created via the construct are obviously not.
java.lang.ThreadGroup is degraded
In Java 14 and Java 16 , several Thread
and ThreadGroup
methods were marked as “deprecated for removal”. You can find out why in the linked sections.
The following of these methods have been deprecated in Java 19:
ThreadGroup.destroy()
– the call to this method is ignored.ThreadGroup.isDestroyed()
– alwaysfalse
returns.ThreadGroup.setDaemon()
– sets thedaemon
flag, but this no longer has any effect.ThreadGroup.getDaemon()
- returns the value of the unuseddaemon
flag.ThreadGroup.suspend()
,resume()
andstop()
throw aUnsupportedOperationException
.
Other changes in Java 19
In this section you will find changes/enhancements that may not be relevant to all Java developers.
Linux/RISC-V Port
Due to the increasing spread of RISC-V hardware, a port for the corresponding architecture was made available with JEP 422 .
Complete list of all changes in Java 19
In addition to the JDK Enhancement Proposals (JEPs) and class library changes presented in this article, there are numerous minor changes that are beyond the scope of this article. See the JDK 19 Release Notes for a full list .
Conclusion
In Java 19, the long-awaited virtual threads developed in Project Loom have finally found their way into the JDK (albeit initially in preview stage). I hope you’re as excited as I am and can’t wait to start using virtual threads in your projects!
Based on this, structured concurrency (still in the incubator stage) will significantly simplify the management of tasks that are divided into parallel subtasks.
Record patterns have been added to the pattern matching options in instanceof
and that have been gradually developed in the last JDK versions .switch
The preview and incubator features “Pattern Matching for switch”, “Foreign Function & Memory API” and “Vector API” have been sent to the next preview or incubator round.
By default, the output on the console is in the standard encoding of the operating system and may have to be changed using the VM option.
HashMaps offer new factory methods to create maps with enough capacity for a given number of mappings.
As always, various other changes round off the release. You can download Java 19 here .