This session, delivered at Devoxx Poland, covers all the major changes to the Java platform between JDK 12 and JDK 17. All language features are covered, as well as many of the important API changes.
1. OpenJDK 17: Get Ready
For the Next LTS Java
Presented by Simon Ritter, Deputy CTO | Azul Systems Inc.
2. 2
Introduction
Java has changed…
…a lot
• Six-month release cadence
• Eight releases since JDK 9
• More features being delivered faster than ever before
• This session will explore new features added since JDK 11
• Helping you to be ready for JDK 17, the next LTS release
3. 3
A Brief Word on JDK Licensing
• Since JDK 11, the Oracle JDK uses a new license
o Oracle Technology Network License Agreement
• More restrictive in where it can be used freely
o Personal or development use
o Oracle approved applications and Oracle Cloud use
o Any other use requires a Java SE subscription to be purchased
• There are many alternative binary distributions of OpenJDK
o More on this later…
4. 4
Incubator Modules
• Defined by JEP 11
• Non-final APIs and non-final tools
o Deliver to developers to solicit feedback
o Can result in changes or even removal
o First example: HTTP/2 API (Introduced in JDK 9, final in JDK 11)
5. 5
Preview Features
• Defined by JEP 12
• New feature of the Java language, JVM or Java SE APIs
o Fully specified, fully implemented but not permanent
o Solicit developer real-world use and experience
o May lead to becoming a permanent feature in future release
• Must be explicitly enabled
o javac --release 17 --enable-preview ...
o java --enable-preview ...
• Preview APIs
o May be required for a preview language feature
o Part of the Java SE API (java or javax namespace)
7. 7
Switch Expressions (Preview)
• Switch construct was a statement
o No concept of generating a result that could be assigned
• Rather clunky syntax
o Every case statement needs to be separated
o Must remember break (default is to fall through)
o Scope of local variables is not intuitive
8. 8
Old-Style Switch Statement
int numberOfLetters;
switch (day) {
case MONDAY:
case FRIDAY:
case SUNDAY:
numberOfLetters = 6;
break;
case TUESDAY:
numberOfLetters = 7;
break;
case THURSDAY:
case SATURDAY:
numberOfLetters = 8;
break;
case WEDNESDAY:
numberOfLetters = 9;
break;
default:
throw new IllegalStateException("Huh?: " + day); };
9. 9
New-Style Switch Expression
int numberOfLetters = switch (day) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
case THURSDAY, SATURDAY -> 8;
case WEDNESDAY -> 9;
default -> throw new IllegalStateException("Huh?: " + day);
};
10. 10
New Old-Style Switch Expression
int numberOfLetters = switch (day) {
case MONDAY:
case FRIDAY:
case SUNDAY:
break 6;
case TUESDAY
break 7;
case THURSDAY
case SATURDAY
break 8;
case WEDNESDAY
break 9;
default:
throw new IllegalStateException("Huh?: " + day);
};
11. 11
Switch Expression: Code Blocks
int levelResult = switch (level) {
case 1 -> {
var x = computeFrom(level);
logger.info("Level 1 alert");
break x;
}
case 2 -> {
var x = negativeComputeFrom(level);
logger.info("Level 2 alert");
break x;
}
default -> throw new IllegalStateException("What level?: " + level);
};
12. 12
Streams
• New collector, teeing
o teeing(Collector, Collector, BiFunction)
• Collect a stream using two collectors
• Use a BiFunction to merge the two collections
Collector 1
Collector 2
BiFunction
Stream Result
15. 15
Text Blocks (Preview)
String webPage = """
<html>
<body>
<p>My web page</p>
</body>
</html> """;
System.out.println(webPage);
$ java WebPage
<html>
<body>
<p>My web page</p>
</body>
</html>
$
incidental white space
Must be followed by newline
Any trailing whitespace is stripped
16. 16
Text Blocks (Preview)
String webPage = """
<html>
<body>
<p>My web page</p>
</body>
</html>
""";
System.out.println(webPage);
$ java WebPage
<html>
<body>
<p>My web page</p>
</body>
</html>
$
Additional blank line
incidental white space
Intentional indentation
17. 17
Switch Expression
int numberOfLetters = switch (day) {
case MONDAY:
case FRIDAY:
case SUNDAY:
break 6;
case TUESDAY
break 7;
case THURSDAY
case SATURDAY
break 8;
case WEDNESDAY
break 9;
default:
throw new IllegalStateException("Huh?: " + day);
};
18. 18
Switch Expression
int numberOfLetters = switch (day) {
case MONDAY:
case FRIDAY:
case SUNDAY:
yield 6;
case TUESDAY
yield 7;
case THURSDAY
case SATURDAY
yield 8;
case WEDNESDAY
yield 9;
default:
throw new IllegalStateException("Huh?: " + day);
};
20. 20
Simple Java Data Class
class Point {
private final double x;
private final double y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
public double x() {
return x;
}
public double y() {
return y;
}
}
21. 21
Records (Preview)
record Point(double x, double y) { }
record Anything<T>(T t) { } // Generic Record
public record Circle(double radius) {
private static final double PI = 3.142; // Static instance fields are allowed
public double area() {
return radius * PI;
}
}
22. 22
Record Additional Details
• The base class of all records is java.lang.Record
o Records cannot sub-class (but may implement interfaces)
• Object methods equals(), hashCode() and toString() can be overridden
• Records are implicitly final (although you may add the modifier)
• Records do not follow the Java bean pattern
o x() not getX() in Point example
o record Point(getX, getY) // If you must
23. 23
Record Constructors
record Trex(int x, int y) {
public Trex(int x, int y) { // Canonical constructor
if (x < y)
System.out.println("inverted values");
this.x = x; // This line needed
this.y = y; // This line needed
}
}
record Range(int low, int high) {
public Range { // Compact constructor
if (low > high)
throw new IllegalArgumentException("Bad values");
}
}
Compact constructor can only
throw unchecked exception
24. 24
Record Constructors
record Trex(int x, int y) {
public Trex(int x, int y, int z) throws TrexException { // Standard constructor
this(x, y); // This line must be present
if (x < y)
throw new TrexException(); // Checked Exception
}
}
Constructor signature must be
different to canonical
27. 27
Pattern Matching instanceof (Preview)
if (obj instanceof String s)
System.out.println(s.length());
else
// Use of s not allowed here
if (obj instanceof String s && s.length() > 0)
System.out.println(s.length());
// Compiler error
if (obj instanceof String s || s.length() > 0)
System.out.println(s.length());
29. 29
Pattern Matching instanceof (JEP 394)
• Be careful of scope!
29
class BadPattern {
String s = "One";
void testMyObject(Object o) {
if (o instanceof String s) {
System.out.println(s); // Prints contents of o
s = s + " Two"; // Modifies pattern variable
}
System.out.println(s); // Prints "One"
}
}
30. 30
Text Blocks
• Second preview
• Two new escape sequences
String continuous = """
This line will not
contain a newline in the middle
and solves the extra blank line issue
""";
String endSpace = """
This line will not s
lose the trailing spaces s""";
31. 31
Foreign-Memory Access API (JEP 393)
• API for safe and efficient access to memory outside of the Java heap
• MemorySegment
o Models a contiguous area of memory
• MemoryAddress
o Models an individual memory address (on or off heap)
• MemoryLayout
o Programmatic description of a MemorySegment
31
try (MemorySegment segment = MemorySegment.allocateNative(100)) {
for (int i = 0; i < 25; i++)
MemoryAccess.setIntAtOffset(segment, i * 4, i);
}
32. 32
Foreign-Memory Access API (JEP 393)
• Example using MemoryLayout and VarHandle
o Simpler access of structured data
32
SequenceLayout intArrayLayout
= MemoryLayout.ofSequence(25,
MemoryLayout.ofValueBits(32,
ByteOrder.nativeOrder()));
VarHandle indexedElementHandle
= intArrayLayout.varHandle(int.class,
PathElement.sequenceElement());
try (MemorySegment segment = MemorySegment.allocateNative(intArrayLayout)) {
for (int i = 0; i < intArrayLayout.elementCount().getAsLong(); i++)
indexedElementHandle.set(segment, (long) i, i);
}
33. 33
Helpful NullPointerException
• Who's never had an NullPointerException?
• Enabled with -XX:+ShowCodeDetailsInExceptionMessages
a.b.c.i = 99;
Exception in thread "main" java.lang.NullPointerException
at Prog.main(Prog.java:5)
Exception in thread "main" java.lang.NullPointerException:
Cannot read field "c" because "a.b" is null
at Prog.main(Prog.java:5)
34. 34
Packaging Tool
• Originally part of JavaFX, which was removed in JDK 11
• Leverages other tools
o jlink for minimised JDK
o Platform specific packaging tools like rpm on Linux, pkg on Mac
• Multiple command line options
jpackage
--name PDFShow
--app-version ${RELEASE_VERSION}
--license-file LICENSE.txt
--vendor "${COMPANY_NAME}"
--type "${inst_format}"
--icon src/main/resources/images/logo.${icon_format}
--input target
--main-jar pdfshow-${RELEASE_VERSION}-jar-with-dependencies.jar
--linux-shortcut --linux-menu-group Office
36. 36
Java Inheritance
• A class (or interface) in Java can be sub-classed by any class
o Unless it is marked as final
Shape
Triangle Square Pentagon
37. 37
Sealed Classes (JEP 360)
• Preview feature
• Sealed classes allow control over which classes can sub-class a class
o Think of final as the ultimate sealed class
• Although called sealed classes, this also applies to interfaces
38. 38
Sealed Classes (JEP 360)
• Uses contextual keywords
o New idea replacing restricted identifiers and keywords
o sealed, permits and non-sealed
• Classes must all be in the same package or module
public sealed class Shape permits Triangle, Square, Pentagon { ... }
Shape
Triangle Square Pentagon Circle
X
39. 39
Sealed Classes (JEP 360)
• All sub-classes must have inheritance capabilities explicitly specified
// Restrict sub-classes to defined set
public sealed class Triangle permits Equilateral, Isosoles extends Shape { ... }
// Prevent any further sub-classing
public final class Square extends Shape { ... }
// Allow any classes to sub-class this one (open)
public non-sealed class Pentagon extends Shape { ... }
40. 40
Records (Second Preview)
• Record fields are now (really) final
o Cannot be changed via reflection (will throw IllegalAccessException)
• Native methods now explicitly prohibited
o Could introduce behaviour dependent on external state
41. 41
Records (Second Preview)
• Local records
o Like a local class
o Implicitly static (also now applies to enums and interfaces)
List<Seller> findTopSellers(List<Seller> sellers, int month) {
// Local record
record Sales(Seller seller, double sales) {}
return sellers.stream()
.map(seller -> new Sales(seller, salesInMonth(seller, month)))
.sorted((s1, s2) -> Double.compare(s2.sales(), s1.sales()))
.map(Sales::seller)
.collect(toList());
}
42. 42
Records (Second Preview)
• Records work with sealed classes (interfaces)
public sealed interface Car permits RedCar, BlueCar { ... }
public record RedCar(int w) implements Car { ... }
public record BlueCar(long w, int c) implements Car { ... }
44. 44
Pattern Matching instanceof
• Now a final feature (as are Records in JDK 16)
• Two minor changes to previous iterations
o Pattern variables are no longer explicitly final
o Compile-time error to compare an expression of type S against a pattern of type T where S is a sub-type of T
static void printColoredPoint(Rectangle r) {
if (r instanceof Rectangle rect) {
System.out.println(rect);
}
}
| Error:
| pattern type Rectangle is a subtype of expression type Rectangle
| if (r instanceof Rectangle rect) {
| ^-------------------------^
45. 45
Streams mapMulti
• Similar to flatMap
o Each element on the input stream is mapped to zero or more elements on the output stream
o Difference is that a mapping can be applied at the same time
o Uses a BiConsumer
• 1 to (0..1) example
45
Stream.of("Java", "Python", "JavaScript", "C#", "Ruby")
.mapMulti((str, consumer) -> {
if (str.length() > 4)
consumer.accept(str.length()); // lengths larger than 4
})
.forEach(i -> System.out.print(i + " "));
// 6 10
48. 48
Stream toList()
• Simplified terminal operation that avoids explicit use of collect()
List l = Stream.of(1, 2, 3)
.collect(Collectors.toList());
List l = Stream.of(1, 2, 3)
.toList();
49. 49
Period of Day
• More variation than simple A.M. or P.M.
• More descriptive
jshell> DateTimeFormatter.ofPattern("B").format(LocalTime.now())
$3 ==> "in the afternoon"
50. 50
Vector API (Incubator Module)
• Not to be confused with the Vector collection class
• API to express vector computations
o Compile at runtime to optimal hardware instructions
o Deliver superior performance to equivalent scalar operations
• Ideally, this would not be necessary
o Compiler should identify where vector operations can be used
50
10
14
11
8
12
16
13
10
+2
+2
+2
+2
10 14 11 8
+2
12 16 13 10
+2
+2 +2
51. 51
Code Example
private void addArraysIfEven(int a[], int b[]) {
for (int i = 0; i < a.length; i++)
if ((b[i] & 0x1) == 0)
a[i] += b[i];
}
53. 53
Code Example With Vector API
private void addArraysIfEven(int a[], int b[]) {
VectorSpecies<Integer> species = IntVector.SPECIES_256;
for (int i = 0; i < a.length; i += species.length()) {
if ((b[i] & 0x1) == 0) {
var mask = species.indexInRange(i, a.length);
var vectorA = IntVector.fromArray(species, a, i, mask);
var vectorB = IntVector.fromArray(species, b, i, mask);
var vectorC = vectorA.add(vectorB);
vectorC.intoArray(a, i, mask);
}
}
}
55. 55
Foreign Linker API (JEP 389): Incubator
• Provides statically-typed, pure-Java access to native code
o Works in conjunction with the Foreign Memory Access API
o Initially targeted at C native code. C++ should follow
• More powerful when combined with Project Panama jextract command
55
public static void main(String[] args) throws Throwable {
var linker = CLinker.getInstance();
var lookup = LibraryLookup.ofDefault();
// get a native method handle for 'getpid' function
var getpid = linker.downcallHandle(lookup.lookup("getpid").get(),
MethodType.methodType(int.class),
FunctionDescriptor.of(CLinker.C_INT));
System.out.println((int)getpid.invokeExact());
}
56. 56
Warnings for Value-Based Classes
• Part of Project Valhalla, which adds value-types to Java
o Introduces the concept of primitive classes
• Primitive wrapper classes (Integer, Float, etc.) designated value-based
o Constructors were deprecated in JDK 9
o Now marked as for removal
o Attempting to synchronize on an instance of a value-based class will issue a warning
56
58. 58
Pattern Matching for switch
• Switch is limited on what types you can use (Integral values, Strings, enumerations)
• This is now expanded to allow type patterns to be matched
o Like pattern matching for instanceof
void typeTester(Object o) {
switch (o) {
case null -> System.out.println("Null type");
case String s -> System.out.println("String: " + s);
case Color c -> System.out.println("Color with RGB: " + c.getRGB());
case int[] ia -> System.out.println("Array of ints, length" + ia.length);
default -> System.out.println(o.toString());
}
}
59. 59
Pattern Matching for switch (Completeness)
void typeTester(Object o) {
switch (o) {
case String s -> System.out.println("String: " + s);
case Integer i -> System.out.println("Integer with value " + i.getInteger());
}
}
void typeTester(Object o) {
switch (o) {
case String s -> System.out.println("String: " + s);
case Integer i -> System.out.println("Integer with value " + i.getInteger());
default -> System.out.println("Some other type");
}
}
void typeTester(Shape shape) { // Using previous sealed class example
switch (shape) {
case Triangle t -> System.out.println("It's a triangle");
case Square s -> System.out.println("It's a square");
case Pentagon p -> System.out.println("It's a pentagon");
}
}
60. 60
Guarded Patterns
void shapeTester(Shape shape) { // Using previous sealed class example
switch (shape) {
case Triangle t && t.area() > 25 -> System.out.println("It's a big triangle");
case Triangle t -> System.out.println("It's a small triangle");
case Square s -> System.out.println("It's a square");
case Pentagon p -> System.out.println("It's a pentagon");
}
}
GuardedPattern:
PrimaryPattern && ConditionalAndExpression
62. 62
Removed From The JDK
• JDK 14: CMS Garbage Collector
o You should really be using G1 (or Azul Prime)
• JDK 15: Nashorn scripting engine
o JavaScript from Java?
• JDK 17: Experimental AOT and JIT compilers
o Didn't shown much appeal
• JDK 17: Deprecate the Security Manager for removal
o No, it doesn't make Java less secure
63. 63
Internal JDK APIs
• JDK 9 introduced encapsulation of internal JDK APIs
o Never intended for general developer use
o Too difficult for backwards compatibility
- Off by default
- Controlled by --illegal-access flag
• JDK 16 took this one step further
o Default became deny access
o Access could still be turned back on
• JDK 17 completes strong encapsulation (almost)
o The --illegal-access flag now has no effect (just a warning)
o Critical APIs (like sun.misc.Unsafe) are still accessible
65. 65
Azul Platform Core / Azul Zulu Builds of OpenJDK
• Enhanced build of OpenJDK source code
o Fully TCK tested
o JDK 6, 7, 8, 11, 13 and 15 supported with updates
• Wide platform support:
o Intel 64-bit Windows, Mac, Linux
o Intel 32-bit Windows and Linux
• Real drop-in replacement for Oracle JDK
o Many enterprise customers
o No reports of any compatibility issues
66. 66
Azul Platform Core Extended Support
• Backporting of bug fixes and security patches from supported OpenJDK release
• Azul Zulu Builds of OpenJDK 8 supported until December 2030
• LTS releases have 9 years active + 2 years passive support
• JDK 15 is a Medium Term Support release
o Bridge to next LTS release (JDK 17)
o Supported until 18 months after JDK 17 release
67. 67
Conclusions
• The six-month release cycle is working well
• The language is developing to address some developerpain-points
• There are some other new features we have not been able to cover
o JVM specific things
• Use Azul Platform Core, with Azul Zulu builds of OpenJDK, if you want to deploy to production