Still Stuck on Java 11? Here’s What You’re Missing | by Matthew Lucas | Mar, 2022

Photo by RShunev on Unsplash

Java 11 has been around a surprising amount of time. It has been the long-term support release (LTS) for close to four years now. Although fresh updates arrive biannually, it’s not unusual for many companies to avoid these interim releases. They instead prefer to stick to more heavily supported versions of the language.

According to the recent Java Developer Productivity Report 2022 from JRebel, just 12% of developers use Java 12 or greater in their applications, with Java 8 and 11 taking a 37% and 29% share, respectively (other JVM languages ​​make up the rest).

“… just 12% of developers use Java 12 or greater…” — JRebel 2022

The future does look brighter for us coders, though. As of September 2021, Java 17 became the new long-term support release. This provides more guarantees and enables risk-averse enterprises to push forward with their tech stack. In fact, according to JRebel, more than 60% of developers expect to upgrade to the latest Java LTS within the next 12 months.

The last few years have felt endless and it’s easy to forget what features Java has released in that time. In the rest of this article, we’ll take a brief tour through the various versions, from 11 to 17, and remind ourselves of the treats that lie in wait.

Before we begin though, it’s worth noting that we’ll focus primarily on core language features, not preview or experimental ones. The focus here is on tooling you can use in your day job with confidence, rather than glimpses of a possible future.

Rather than diving straight in — and because so many developers are starting out from Java 8 — let’s have a quick reminder of what Java 11 gave us.

Run file with a single command

Rather than having to go through a write-compile-execute loop to spin up your code it’s possible, from Java 11 onward, to run code straight from the source file as part of the java command.

$ java RunMe.java
Yoo hoo!

Not particularly useful for larger projects, but helpful if you want to write small utility programs, scripts, and the like.

You can even include a shebang header in the file, and run it like any other script.

#!/usr/bin/java --source 11

New string methods

A few new utility methods were added to String, including:

  • .isBlank— returns true if empty or containing only whitespace.
  • .lines— converts a String into a Stream of Strings, spit by line separators.
  • .strip — removes all leading and trailing whitespace from a String.
  • .repeat — repeats a String n times to produce a new String.

New file methods

Two convenience methods — readString and writeString — were introduced to read and write a String directly to/from a file. No more need to fiddle around with InputStreams or rely on third-party libraries to achieve this.

Collection to array

A slightly more convenient toArray method that takes an IntFunction rather than an instance of an array.

Not predicate

A convenience method — notto invert a Predicate. The same as the negate function, but more readable in many circumstances (not(valid) is easier on the eye than negate(valid))

Lambda local variable syntax

The ability to use var within a Lambda was included.

Why bother when you can just omit the type information anyway? Well, it allows you to apply modifiers, such as @NotNull, to the argument which isn’t possible when you don’t use any declaration. You could use annotations with the full type, of course, but this is much more concise.

(@NotNull var b) -> b

New HTTP client

A new, built-in, HTTP client can be found in the java.net.http package.

Flight recorder

Until Java 11, the Java Flight Recorder used to be a commercial part of the Oracle JDK. Now it has been open-sourced and is part of the OpenJDK.

It’s a lightweight way of profiling your application and provides statistics about the behavior of the JVM, including memory usage, object allocation, and garbage collection. Once captured, you can feed the output into the Java Mission Control tool to visualize your findings.

New string methods

Java 12 begins by introducing further enhancements to the String API. These additions seem to be focused on the pending arrival of text blocks (see Java 15).

  • .indent — adjusts the indentation of each line by the given offset, which can be positive or negative.
  • .transform — applies a transformation function to the String producing any type.

File mismatch

Files.mismatch gives us a convenience function to find the first mismatching byte in a comparison between two files.

Teeing collector

A Collector is a composite of two downstream Collectors. Every element passed to the resulting Collector is processed by both downstream Collectors, then their results are merged using the specified merge function into the final result.

The classic use case here is to calculate averages. All values ​​are passed to two downstream Collectors, a sum and a count, with the final results aggregated together (sum/count).

New number formatter

The compact number formatter provides a localized, short-form, representation of a number — think “10K” or “10 thousand”.

Let’s skip this one, shall we? Java 13 didn’t introduce any new language features.

Switch expressions

Switch expressions give us the useful ability to assign the result of a switch statement to a variable. This is (hopefully) the first step on the journey to more powerful pattern matching, as you’d find in Scala, Rust or other functional/functional-inspired languages.

Helpful NullPointers

A NullPointerException in a production log can strike fear into even the most hardy developer. Prevention is better than cure (nulls are argubly a huge mistake), but at least Java now gives us more detailed error messages in the case of an NPE — for example Cannot invoke “String.toLowerCase()” because “missing” is null.

Text Blocks

At last! Java has caught up with pretty much every other language by supporting text blocks. No more chains of “…n” + “…n” which should simplify things like constructing DB queries, JSON blocks in tests, etc.

Hidden Classes

This is a bit of an arcane one. Java 15 introduces the ability to use classes that cannot be linked by other classes or discovered via reflection. They are, however, very efficient, and that’s the attraction. Mainly for use by people who work with dynamic bytecode, or JVM languages ​​(Scala, Groovy, Clojure, etc).

For example, from Java 15, lambda expressions use hidden classes under the hood which makes them more efficient.

Shenandoah

Shenandoah is a low pause time garbage collector that reduces GC pause times at the cost of higher CPU utilization by performing more garbage collection work concurrently with the running Java program.

Although it had been in preview status for a number of releases, it’s finally ready for prime time.

ZGC

Another production-ready garbage collector with similar goals to Shenandoah. The Z Garbage Collector (ZGC) is a very scalable low latency garbage collector. ZGC performs all expensive work concurrently, without stopping the execution of application threads for more than 10ms, which makes it suitable for applications that require low latency and/or use a very large heap (up to multi-terabytes).

Day period

The DateTimeFormatter supports a new symbol B as an alternative day-period to am/pm. For example, “in the morning” or “in the afternoon”.

Stream.toList

Rather than using .collect(Collectors.toList()) to turn a Stream into a List, the Stream API introduces a convenience method .toList to achieve the same end.

Records

Records are another big addition to the language and, similar in some ways to an enum, are a stricter form of a class that specifically promotes immutability.

Creating immutable data objects in Java has always been a bit cumbersome. You have to ensure that all fields are marked as final, and that the appropriate equals / hashCode / toString implementations are present and kept in check.

Records make this a lot easier by providing us with an immutable construct out of the box. Fields aren’t able to be modified plus accessors, equals, hashCode and toString are all provided to you for free.

Pattern matching for instanceof

Rather than having to litter your code with casts after an instanceof check, this simple form of pattern matching provides you with the matched object, correctly typed and ready to go.

Local enums

Java 15 allows us to create enums scoped to a method. Although the uses are likely fairly limited, it may prove useful to label data within the context of a function.

Local interfaces

Similar to the previous feature we can also create interfaces, and implement them with anonymous classes, within the context of a method.

Sealed classes/intefaces

The biggest feature in Java 17 is that of sealed classes. Similar in some ways to an enum, sealed classes restrict the set of classes that can extend or implement a class/interface.

The main difference between enums and closed classes, is that the former restrict you to a fixed set of instances. Sealed classes take this one step further by restricting you to kinds of values ​​of which there may be multiple instances.

For example, a pride of lions couldn’t be represented by an enum value, because there would be only one lion. A sealed interface, alternatively, would allow many different lions to exist whilst still respecting the restriction on the type.

This can help to enhance the type safety of the language and will again add to Java’s upcoming pattern matching feature(s). By knowing the complete set of classes that can implement an interface, the compiler can make stronger guarantees that you’ve satisfied every edge case in, for example, a switch statement. No more use of default handling required, because the compiler could catch an error at compile time rather than runtime.

Restore always strict floating point semantics

This is mainly targeted at scientific applications and makes floating-point operations consistently strict. This was already the case prior to Java 1.2 but had to be weakened due to hardware architecture challenges. It has now been restored.

Java 18 was released a week ago at the time of writing, on March 22nd, 2022. It provides thousands of performance, stability, and security improvements, although no major language features this time (notwithstanding UTF-8 by default and the inclusion of a Simple Web Server. This is not an LTS release but does continue Java’s fast-paced release cadence. There are, as always, a number of preview features included too — see https://www.oracle.com/news/announcement/oracle-releases-java-18-2022-03-22/ for more details on those.

As far the long-term support releases of Java, Oracle has recently proposed accelerating this from the current 3-year cycle to a shorter 2-year period. This would result in Java 21 being the next LTS rather than Java 23 and should increase the pace of adoption allowing many more of us to start using the latest and greatest features in the language much earlier than would’ve been previously possible.

Leave a Comment