2023-10-13

Tags: Java

My initial reference major Java releases. Its focus is for application developers, so the focus is on Long-Term-Stable (LTS) releases we should be running on. Also, the focus is more on language features than on API changes. Java has a view into specific API changes for each release since JDK 11 at https://docs.oracle.com/en/java/javase/21/docs/api/new-list.html (which has checkboxes to zero in on specific releases).

While I captured some of the preview & incubator features relative to releases, they exist to expose developers to a concept and gather feedback prior to general releases. They usually require special flags to be run and thus should not be relied on for production application deployments.

Revision History

Table 1. Java Release History
Version When Features

JDK 21 (LTS)

2023-SEP

Pattern Matching for Records & switch, Sequenced Collections, Virtual Thread
CDS (Class Data Sharing), jwebserver
deprecations: Object.finalize(), Applets & Security Manager
preview: String templates, Scoped Values, Foreign Function & Memory API, unnamed classes/instance main

17 (LTS)

2021-SEP

supported by Spring 6 & Spring Boot 3
Records, switch expressions, "helpful" NullPointerExceptions, Text Blocks, Pattern Matching for instanceof, Sealed Classes
enhance: Records (may be member of inner class), add Stream.toList(), packaging tool
stronger encapsulation of internals (com.sun.*, jdk.*, org.*)
extract Nashorn (JavaScript engine)
incubator Vector API (SIMD: single instruction, multiple data)

11 (LTS)

2018-SEP

enhance String/Collection/Files/Optional
run single-file without compile, tool "Flight Recorder" (JVM/OS events & visualizer)
deprecate EE/CORBA/Nashorn/applets/JavaFX

10

2017

local variable type inference (var list = new ArrayList<String>();)

9

2017-SEP

Project Jigsaw (module system)
enhance Collection/Stream/Process, interface private methods
tools: jshell, jlink

8

2014-MAR

Streams, Lambdas, default methods, Optional
JavaScript (Nashorn runtime), Date & time API updates

7

2011-JUL

switch Strings, try-with-resources, diamond operator, dynamic lang support (invokedynamic),

6

2006-DEC

performance, enhance annotations, upgrade JAXB (StAX parser), JDBC4, Rhino JavaScript engine

5

2004-SEP

Generics (type casts/conversions), Autoboxing (convert primitives to objects & back)
Annotations, Enumerations (enum), varargs (String…​ lines), enhanced for..each, static imports, java.util.concurrent

1.4

2002-FEB

RegEx, logging API, JAXP (XML support), exception chaining
assert keyword, NIO (non-blocking IO), java.util.prefs, integrated security/crypto libs

1.3

2000-MAY

JNDI, JavaSound, RMI compatibility with CORBA, JPDA debugger API

1.2

1998-DEC

Collections framework, Swing UI, strictfp keyword, Java IDL (CORBA support)

1.1

1997-FEB

JDBC, JavaBeans, AWT, Inner classes, RMI, read-only reflection

1.0.2

1996-JAN

browser "WebRunner", networking

Examples & Notes

I was initialy unimpressed by Virtual Threads because Functional Programming lets me avoid directly manipulating and managing threads. However, it was pointed out that they really shine for I/O blocked threads such as Web Applications. They default to using Fork Join Pools with a FIFO queue. If necessary, you can configure ot LIFO queue or override the pool to manage behavior (but "EEK!" for most application development). Virtual Threads probably aren’t a huge benefit for compute or CPU-bound threads, where "platform threads" will suffice.

CDS or Class Data Sharing didn’t sound interesting until a presenter pointed out it could simplify CI/CD pipelines to obtain a 10-20% startup benefit in time or memory for extremely little work.

Sequenced Collections can really help application development (and greatly simplify interview questions) by adding methods reversed(), addFirst/Last(), getFirst/Last(), removeFirst/Last(). SequenedMaps adds corresponding methods appropriate for SortedMaps and NavigableMaps.

Pattern Matching

Another convenience to save lines of code and reduce errors. Initially introduced for the instanceof operator, it works in switch in JDK 21.

instanceof Pattern Matching
jshell> Object obj = new String("my string");
obj ==> "my string"

// PREVIOUS
jshell> if (obj instanceof String) {
   ...>     String s = (String) obj;
   ...>     System.out.println(s);
   ...> }
my string

// NOW
jshell> if (obj instanceof String s) {
   ...>     System.out.println(s);
   ...> }
my string

In switch Pattern Matching, the compiler enforces exhaustive cases, thus may require a case default → {…​}. The JDK also added a when keyword so Record Patterns can do something like case Name name(var first, var mid, var last) when !mid.isEmpty() → {…​}; to reduce errors and contorting code.

switch Pattern Matching
jshell> public record Car(int numSeats, String vin) {};
|  created record Car
jshell> var c = new Car(5, "my-vin");
c ==> Car[numSeats=5, vin=my-vin]

jshell> static double getValue(Object o) {
   ...>     return switch(o) {
   ...>         case Integer i -> i.doubleValue();
   ...>         case Float f -> f.doubleValue();
   ...>         case String s -> Double.parseDouble(s);
   ...>         case Car c -> c.numSeats();
   ...>         default -> 0d;
   ...>     };
   ...> }
|  created method getValue(Object)

jshell> getValue(c)
$7 ==> 5.0
jshell> getValue("17.01")
$8 ==> 17.01
jshell> getValue(3)
$9 ==> 3.0

Please excuse the horrible example, but it illustrates the new abilities to match by type and not require break statements if using statement expressions.

Text Blocks

Text Blocks equals multi-line strings with less punctuation in source code, including escaping characters. The preview (requires flag to work) for String Templates provides a safer string interpolation, but uses {varName} instead of ${varName} used in many other languages.

Basic Text Block (multi-line String)
jshell> String htmlTextBlock = """
   ...> <!DOCTYPE html>
   ...> <html lang="en">
   ...> <body>
   ...> </body>
   ...> </html>""";
htmlTextBlock ==> "<!DOCTYPE html>\n<html lang=\"en\">\n ... \n</body>\n</html>"
String Template Example (requires preview flag --enable-preview)
jshell> String name = "IT Gumby";
name ==> "IT Gumby"
jshell> Integer num = 42;
num ==> 42

jshell> String template = STR."""
   ...> Hello, friend!
   ...> My name is \{name} and the answer is always \{answer}
   ...> """;
template ==> "Hello, friend!\nMy name is IT Gumby and the answer is always 42\n"

Records

Write less code (thus less bugs) for immutable objects. Records automatically get a constructor, equals(), toString() and "getters" that do NOT conform to the JavaBean standard (named after field without "get" prefix). For this reason, libraries such as jackson require newer versions to support records.

Record example
jshell> public record Book(String title, String author, String isbn) {};
|  created record Book
jshell> Book theHobbit= new Book("The Hobbit", "J.R.R. Tolkien", "0345339681");
theHobbit ==> Book[title=The Hobbit, author=J.R.R. Tolkien, isbn=0345339681]
jshell> theHobbit.author();
$1 ==> "J.R.R. Tolkien"

jshell> import java.lang.reflect.*;
jshell> for(var m : Book.class.getDeclaredMethods()) { System.out.println(m.getName());}
// => equals, toString, hashCode, title, author, isbn

deprecation of Object.finalize()

This has been deprecated because in some situations, execution of the finalize() method was unpredicatable. Instead, implemement java.lang.Closeable (implement a close() method) or use java.lang.ref.Cleaner released in JDK 17 Cleaner.

Sealed Classes

Formerly known as Project Amber, sealed classes restrict which other classes & interfaces can extend or implement a sealed component.

New keywords: sealed …​ permits (explicit subclassing), final (prevent subclassing), non-sealed (any can inherit)

For now, I am skeptical about sealed classes because changes have to cross multiple classes or files. Instead,https://en.wikipedia.org/wiki/Composition_over_inheritance["Favor composition over inheritance"].

Managing local Java development versions

I use brew (to install jenv), sdk and jenv to easily switch between java versions.

sdk list java | grep 21
sdk install java 21-open ## not default yet, waiting for 1st patch release
jenv add ~/.sdkman/candidates/java/21-open/
jenv local 21 ## creates .java-version in current folder
java -version
# openjdk version "21" 2023-09-19
# OpenJDK Runtime Environment (build 21+35-2513)
# OpenJDK 64-Bit Server VM (build 21+35-2513, mixed mode, sharing)

Upgrade Ideas

https://docs.openrewrite.org/ and https://www.moderne.io/ propose to write "recipes" to automatically update source code in a consistent manner. It is really worth evaluating this idea. And if you are a library or dependency, it is worth considering adding to the recipes so your users can keep up with less effort.