Mohamed Taman
Chief Solutions Architect, Owner/CEO of SiriusXI, a Java Champion, Oracle ACE, JCP member, Consultant, Speaker, and Author.
Mohamed Taman
Chief Solutions Architect
CEO @SiriusXI | Java Champion | Oracle ACE Alumni | Jakarta EE Ambassador | Author | trainer | speaker.
Highlights the previews and incubators for pattern matching for switch expressions, record expressions, virtual threads, structured concurrency, and the Vector API. And a selection of small enhancements, plus thousands of performance, security, and stability updates.
JDK is more than targeted JEPs.
@_tamanm
|
|
|
@_tamanm
|
|
|
Or by googling 🕵 me
"Mohamed Taman"
@_tamanm
|
|
|
Introduction.
Checkout Project Amber features.
Checkout Project Loom features.
Explore the new, improved Language APIs and others.
Checkout Project Panama features.
Farewell to APIs has gone forever.
@_tamanm
|
|
|
@_tamanm
|
|
|
@_tamanm
|
|
|
@_tamanm
|
|
|
1st Prev JDK 17
3rd Prev JDK 19
2nd Prev JDK 18
Pattern matching for the switch. This is a feature that has already gone through two rounds of previews.
switch (obj) {
case String s && s.length() > 8 -> System.out.println(s.toUpperCase());
case String s -> System.out.println(s.toLowerCase());
case Integer i -> System.out.println(i * i);
default -> {}
}
mohamed_taman -- -bash
First appearing in Java 17, pattern matching for switch allows you to write code like the following:
@_tamanm
|
|
|
To improve the readability of this feature, Java 19 changed the syntax.
In Java 17 and Java 18, the syntax was to write String s && s.length() > 0
; now, in Java 19, instead of &&
, you must use the easier-to-read keyword when
.
switch (obj) {
case String s when s.length() > 8 -> System.out.println(s.toUpperCase());
case String s -> System.out.println(s.toLowerCase());
case Integer i -> System.out.println(i * i);
default -> {}
}
mohamed_taman -- -bash
Therefore, the previous example would be written in Java 19 as the following:
@_tamanm
|
|
|
What’s also new is that the keyword when is a contextual keyword; therefore, it has a meaning only within a case label.
Continue
@_tamanm
|
|
|
1st Prev JDK 19
private void print(Object object) {
if (object instanceof Point point) {
System.out.println("object is a point, x = " + point.x()
+ ", y = " + point.y());
}
// else ...
}
mohamed_taman -- -bash
Record and type patterns can be nested to allow for robust, declarative, and modular data processing.
> Assume you have defined the following Point record:
record Point(int x, int y) { }
> You also have a print()
method that can print any object, including positions:
You might have seen this notation before; it was introduced in Java 16 as pattern matching for instanceof.
@_tamanm
|
|
|
private void print(Object object) {
if (object instanceof Point(int x, int y)) {
System.out.println("object is a point, x = " + x +
", y = " + y);
}
// else ...
}
mohamed_taman -- -bash
As of Java 19, JEP 405 allows you to use a new feature called a record pattern.
> This new addition allows you to write the previous code as follows:
Now can match on Point(int x, int y)
and can then access their x
and y
fields directly.
Continue
@_tamanm
|
|
|
private void print(Object object) {
switch (object) {
case Point point
-> System.out.println("object is a point, x = " + point.x()
+ ", y = " + point.y());
// other cases ...
}
}
mohamed_taman -- -bash
Previously with Java 17, you could also write the original example as a switch statement.
> You can now also use a record pattern in the switch statement:
private void print(Object object) {
switch (object) {
case Point(int x, int y)
-> System.out.println("object is a point, x = " + x +
", y = " + y);
// other cases ...
}
}
mohamed_taman -- -bash
Continue
@_tamanm
|
|
|
private void print(Object object) {
if (object instanceof Line(Point(int x1, int y1), Point(int x2, int y2))) {
System.out.println("object is a Line, x1 = " + x1 + ", y1 = " + y1
+ ", x2 = " + x2 + ", y2 = " + y2);
}
// else ...
}
mohamed_taman -- -bash
It is now possible to match nested records with pattern matching for instanceof.
> Here’s another example that defines a second record, Line
, with a start Point and a destination point, as follows:
public record Line(Point from, Point to) {}
> The print()
method can now use a record pattern to print all the path’s x
and y
coordinates easily:
Continue
@_tamanm
|
|
|
private void print(Object object) {
switch (object) {
case Line(Point(int x1, int y1), Point(int x2, int y2))
-> System.out.println("object is a Line, x1 = " + x1 + ", y1 = " + y1
+ ", x2 = " + x2 + ", y2 = " + y2);
// other cases ...
}
}
mohamed_taman -- -bash
> The print()
method can be written as a switch statement:
It is now possible to match nested records with pattern matching with switch.
Thus, record patterns provide an elegant way to access a record’s elements after a type check.
Continue
@_tamanm
|
|
|
@_tamanm
|
|
|
1st Prev JDK 19
Enable easy troubleshooting, debugging, and profiling of virtual threads with existing JDK tools.
Enable existing code that uses the java.lang.Thread
API to adopt virtual threads with minimal change.
No intention to change the basic Java concurrency model.
No intention to remove the traditional threads implementation, or to silently migrate existing applications to use virtual threads.
@_tamanm
|
|
|
One solution has been to use the reactive programming model with frameworks such as Project Reactor
and RxJava
.
Continue
public DeferredResult<ResponseEntity<?>> createOrder(
CreateOrderRequest createOrderRequest, Long sessionId, HttpServletRequest context) {
DeferredResult<ResponseEntity<?>> deferredResult = new DeferredResult<>();
Observable.just(createOrderRequest)
.doOnNext(this::validateRequest)
.flatMap(
request ->
sessionService
.getSessionContainer(request.getClientId(), sessionId)
.toObservable()
.map(ResponseEntity::getBody))
.map(
sessionContainer ->
enrichCreateOrderRequest(createOrderRequest, sessionContainer, context))
.flatMap(
enrichedRequest ->
orderPersistenceService.persistOrder(enrichedRequest).toObservable())
.subscribeOn(Schedulers.io())
.subscribe(
success -> deferredResult.setResult(ResponseEntity.noContent()),
error -> deferredResult.setErrorResult(error));
return deferredResult;
}
mohamed_taman -- -bash
@_tamanm
|
|
|
Virtual threads will allow you to reimplement the reactive example quite simply as the following:
Continue
public void createOrder(
CreateOrderRequest createOrderRequest, Long sessionId, HttpServletRequest context) {
validateRequest(createOrderRequest);
SessionContainer sessionContainer =
sessionService
.getSessionContainer(createOrderRequest.getClientId(), sessionId)
.execute()
.getBody();
EnrichedCreateOrderRequest enrichedCreateOrderRequest =
enrichCreateOrderRequest(createOrderRequest, sessionContainer, context);
orderPersistenceService.persistOrder(enrichedCreateOrderRequest);
}
mohamed_taman -- -bash
@_tamanm
|
|
|
1st Inc. JDK 19
Promote a style of concurrent programming which can eliminate common risks arising from cancellation and shutdown, such as thread leaks and cancellation delays.
@_tamanm
|
|
|
You could use the Java executable framework, to write concurrent application as in the following:
Continue
private final ExecutorService executor = Executors.newCachedThreadPool();
public Invoice createInvoice(int orderId, int customerId, String language)
throws InterruptedException, ExecutionException {
Future<Customer> customerFuture =
executor.submit(() -> customerService.getCustomerBy(customerId));
Future<Order> orderFuture =
executor.submit(() -> orderService.getOrderBy(orderId));
Future<String> invoiceTemplateFuture =
executor.submit(() -> invoiceTemplateService.getTemplateFor(language));
Customer customer = customerFuture.get();
Order order = orderFuture.get();
String template = invoiceTemplateFuture.get();
return invoice.generate(customer, order, template);
}
mohamed_taman -- -bash
@_tamanm
|
|
|
As you have seen you can pass the three subtasks to the executor
and wait for the partial results. It is easy to implement the basic task quickly, but consider these possible issues:
Continue
All these possible issues can be addressed, but the solution would require complex and difficult-to-maintain code.
@_tamanm
|
|
|
Continue
Invoice createInvoice(int orderId, int customerId, String language)
throws ExecutionException, InterruptedException {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Future<Customer> customerFuture =
scope.fork(() -> customerService.getCustomerBy(customerId));
Future<Order> orderFuture =
scope.fork(() -> orderService.getOrderBy(orderId));
Future<String> invoiceTemplateFuture =
scope.fork(() -> invoiceTemplateService.getTemplateFor(language));
scope.join(); // Join all forks
scope.throwIfFailed(); // ... and propagate errors
Customer customer = customerFuture.resultNow();
Order order = orderFuture.resultNow();
String template = invoiceTemplateFuture.resultNow();
// Here, both forks have succeeded, so compose their results
return invoice.generate(customer, order, template);
}
}
mohamed_taman -- -bash
Using the StructuredTaskScope
class, you can rewrite the previous code as in the following:
@_tamanm
|
|
|
@_tamanm
|
|
|
1st Inc. JDK 17
2nd Inc. JDK 18
1st Prev. JDK 19
@_tamanm
|
|
|
The following examples store a string in off-heap memory, followed by a call to the C standard library’s strlen
function to return the String
length:
Continue
public class ForeignFunctionAndMemoryTest {
public static void main(String[] args) throws Throwable {
// 1. Get a lookup object for commonly used libraries
SymbolLookup stdlib = Linker.nativeLinker().defaultLookup();
// 2. Get a handle on the strlen function in the C standard library
MethodHandle strlen = Linker.nativeLinker().downcallHandle(
stdlib.lookup("strlen").orElseThrow(),
FunctionDescriptor.of(JAVA_LONG, ADDRESS));
// 3. Convert Java String to C string and store it in off-heap memory
MemorySegment str = implicitAllocator()
.allocateUtf8String("Happy Java Conference!");
// 4. Invoke the foreign function
long len = (long) strlen.invoke(str);
System.out.println("len = " + len);
}
}
mohamed_taman -- -bash
@_tamanm
|
|
|
3rd Inc. JDK 18
Along with the complementing vector mask compress operation, Java 19 adds the cross-lane vector operations of compress and expand.
The Foreign Function and Memory API preview defines improvements to loading and storing vectors to and from memory segments as part of the API proposed for JDK 19.
4th Inc. JDK 19
1st Inc. JDK 16
2nd Inc. JDK 17
@_tamanm
|
|
|
@_tamanm
|
|
|
Template interpreter
C2 (server) JIT compiler
JDK 19
All mainline garbage collectors, including ZGC and Shenandoah
@_tamanm
|
|
|
> If that’s not what you want and you’d prefer to see output in UTF-8, add the following JVM options when calling the application:
If you run an existing application with Java 19, you may see ????? on the console instead of special characters. This is because, as of Java 19, the operating system’s default encoding is used for printing to System.out
and System.err
.
JDK 19
-Dstdout.encoding=utf8 -Dstderr.encoding=utf8
> If you don’t want to do this each time the application launches, you can also define the following environment variable (it starts with an underscore) to set these parameters globally:
_JAVA_OPTIONS="-Dstdout.encoding=utf8 -Dstderr.encoding=utf8"
@_tamanm
|
|
|
> To create a HashMap for 180 mappings:
Because the HashMap
has a default load factor of 0.75 when it is initialized. This indicates that the HashMap
gets rebuilt (also called rehashed) with double the size as soon as it is 75% filled. Thus, the new HashMap
is initialized with a capacity of 180, can hold only 135 (180 × 0.75) mappings without being rehashed.
JDK 19
// for 180 mappings: 180 / 0.75 = 240
Map<String, Integer> map = new HashMap<>(240);
> Java 19 makes it easier to create a HashMap
that has the required mappings without fiddling with load factors by using the new static factory method newHashMap(int)
Map<String, Integer> map = HashMap.newHashMap(180);
> Similar labor-saving static factory methods have been created in Java 19. Here’s the complete set:
HashMap.newHashMap
LinkedHashMap.newLinkedHashMap
WeakHashMap.newWeakHashMap
HashSet.newHashSet
LinkedHashSet.newLinkedHashSet
@_tamanm
|
|
|
> For example, the following creates a formatter that may format a date according to a locale, for example, “Feb 2022”
in the US locale and “2022年2月”
in the Japanese locale:
JDK 19
DateTimeFormatter.ofLocalizedPattern("yMMM")
java.time.format.DateTimeFormatter
and DateTimeFormatterBuilder
classes.FormatStyle.FULL
, FormatStyle.LONG
, FormatStyle.MEDIUM
, and FormatStyle.SHORT
.DateTimeFormatter.ofLocalizedPattern(String requestedTemplate)
method.@_tamanm
|
|
|
> For example, the following code now prints zoozoobar
not zoobarzoobar
:
JDK 19
StringBuilder builder = new StringBuilder("zoo");
System.out.println("" + builder + builder.append("bar"));
invokedynamic
.@_tamanm
|
|
|
@_tamanm
|
|
|
Locale japanese = new Locale("ja"); // deprecated
Locale japan = new Locale("ja", "JP"); // deprecated
Locale japanese1 = Locale.of("ja");
Locale japan1 = Locale.of("ja", "JP");
System.out.println("japanese == Locale.JAPANESE = " + (japanese == Locale.JAPANESE));
System.out.println("japan == Locale.JAPAN = " + (japan == Locale.JAPAN));
System.out.println("japanese1 == Locale.JAPANESE = " + (japanese1 == Locale.JAPANESE));
System.out.println("japan1 == Locale.JAPAN = " + (japan1 == Locale.JAPAN));
mohamed_taman -- -bash
> The following example shows the use of the factory method compared to the old constructor:
Locale
class were marked as deprecated. You should use the new static factory method Locale.of()
to ensure only one instance per Locale configuration.When you run this code, you will see that the objects supplied via the factory method are identical to the Locale constants, whereas those created with constructors logically are not.
JDK 19
@_tamanm
|
|
|
java.lang.ThreadGroup
methods degraded> Now, the following methods have been decommissioned in Java 19:
In Java 14 and Java 16, many Thread
and ThreadGroup
methods were marked as deprecated for removal.
JDK 19
ThreadGroup.destroy()
invocations will be ignored.ThreadGroup.isDestroyed()
always returns false.ThreadGroup.setDaemon()
sets the daemon flag, but this has no effect.ThreadGroup.getDaemon()
returns the value of the unused daemon flags.ThreadGroup.suspend()
, resume()
, and stop()
throw an UnsupportedOperationException
.@_tamanm
|
|
|
By Mohamed Taman
Java is innovative with each release, and to become a creative engineer for your company, you should know what is new in your language. This session will sweep the dust off Java SE's 19 hidden gems, including new cool language features, compiler changes, library additions, and critical bug fixes. They're handy in your day-to-day work.
Chief Solutions Architect, Owner/CEO of SiriusXI, a Java Champion, Oracle ACE, JCP member, Consultant, Speaker, and Author.