Tracking the evolution from Java SE 9 to version 12

Starting with the major release of Java SE 9+, each new six-month release of the JDK means that the programming language and virtual machine is moving forward quickly in order to cope with the rate of new technology innovation. Each version since 9, through Java SE 12, includes a bundle of changes, more library additions, new language features, and alterations to the JDK clean-up process.

Let me take you on an exploration of the most significant updates. I’ve constructed this article with two main sections:

  • Features that are visible to all developers (which are so useful, you’ll wonder how you coded without them)
  • Everything else (the “under the hood” features)

And I finish off the tour by showing you the modules and methods that have been deprecated or removed, a useful tool if you’re planning to migrate your applications to the latest version.

Unless I’ve tagged a feature specifically for JDK 12, you can assume it will work in version 11.

Let’s start with the developer features.

New features visible to developers

I’ve separated the new features in Java SE 11 and 12 into two categories: APIs and methods, and language features.

APIs and methods

To illustrate the new APIs and methods, I’ll open a JShell session. JShell is an interactive REPL Java shell (read-eval-print-loop) that was added in Java 9. For more information about this technology, you might skip ahead and try the following tutorials in this series articles:

For now, you’ll just see it in action.

In this section, you’ll be seeing:

  • String family classes
  • IO/NIO classes
  • Streams and Optional classes
  • Utility classes
  • Security-related class changes

String family classes

Let’s start with the new methods that have been added to all the classes that deal with strings, which we use in code every day.


The java.lang.Character class has a new method: StringtoString(int);. It is an overload for the existing method in the String class, but it takes an int Unicode code point instead of a char.


The java.lang.CharSequence class has a new method: int compare(CharSequence, CharSequence);. It compares two CharSequence instances lexicographically and returns a negative value, a zero, or a positive value if the first sequence is lexicographically less than, equal to, or greater than the second, respectively.

java.lang.StringBuffer and StringBuilder

The java.lang.StringBuffer and StringBuilder classes have a new method: compareTo();. It behaves exactly the same as the previous compare() method of CharSequence but takes a StringBuffer/StringBuilder as parameter respectively.


java.text.NumberFormat (JDK 12) introduces NumberFormat which adds support for formatting a number in its compact form.

Compact number formats refers to the representation of a number in a short or human-readable form. For example, in the en_US locale, 1000 can be formatted as “1K,” and 1000000 as “1M,” depending on the style used, which is specified by NumberFormat.Style. It is defined inthe Unicode Local Data Markup Language specification.

To obtain the instance, use one of the factory methods given by NumberFormat for compact number formatting:

jshell> import java.text.NumberFormat
.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT)
$1 ==> "10K"

In my opinion, this class is one of the most important highlights of the new APIs in JDK 11. There are several new methods here you will find useful in your programming:

  • String repeat(int);
  • booleanisBlank();
  • String strip();
  • Stream lines():

The String repeat(int); name is pretty self-explanatory. It’s an instance method, so you can call it on an actual string. In this example, I will simulate my youngest daughter replying “no” to me when I ask her for something. She is a fan of the Batman Lego movie, so I have built a string saying no that will repeat 14 times and add the word Batman to the end:

jshell> "no ".repeat(14).trim().concat(", Batman!")
$1 ==> "no nonononononononononononono, Batman!"

That’s right! You get the Batman theme song with just a single method invocation and string concatenation.

booleanisBlank(); returns true if the string is empty or contains only white space codepoints, otherwise it returns false. Let’s use the old way to determine if the string contains a string or just spaces by creating a string variable called blankText using the var keyword that was introduced in Java 10. Initialize it with a string containing just some spaces:

jshell>varblankText = "      "
blankText ==> "      "

jshell>blankText.trim().length() == 0
$2 ==> true

$3 ==> true

As you can see, that works and gives us true. However, this code isn’t really intention-revealing. As of Java 11, you can use the isBlank() method on the string directly.

To illustrate String strip();, create a new string variable called textWithSpaces. The string should contain a new line, a tab, some spaces, then the word Text. After that, some more spaces and end with a special Unicode space character. Often, you want to clean up the text and get rid of all the whitespace. You can do that using the new strip method.

jshell>vartextWithSpaces = "\n \t      Text \u2005"
textWithSpaces ==> "\n \t      Text  "

$4 ==> "Text"

Let’s call strip() on text and you will get back the word Text without any whitespace.

But wait, you say, didn’t we use trim() in the previous example to achieve the same outcome? That is a sharp observation. So, let’s see what happens if you call trim() on the variable.

$5 ==> "Text  "

And as you can see, the new line, the tab, and the spaces before the text have been removed. However, the other spaces and the last special Unicode space are left intact. That’s because the trim implementation isn’t aware of all the Unicode whitespace code points, so it just assumes that the Unicode code point at the end of the string is a text character and the string can’t be trimmed any further.

The strip implementation, as we’ve seen, is smarter about this. It’s actually based on the Character.isWhitespace() method. So, when we pass in the same Unicode code point to the isWhitespace() method, we get back true:

$6 ==> true

This is whitespace. And the same will happen for all other Unicode whitespace code points. Also, there are two corresponding implementations called String stripLeading() and String stripTrailing() and they respectively remove the whitespace from the beginning and the end of the string. All three strip methods are Unicode whitespace-aware.

The last new string method I want to discuss is Stream lines(). To see how it operates, you are going to create a multi-line string containing one, two, three, and four, separated by a new line. Then you call the lines() method on the string and get back a stream of strings. Each line is an element in a stream.

jshell> "\nOne\nTwo\nThree\nFour".lines().forEach(System.out::println)


This makes it easy to process the multi-line string line by line. In this example, you’re going to call forEach() on the stream and pass in System.out::println as a method reference. This prints each line separately to the console.

IO/NIO classes

Let’s continue with the new methods that have been added to all the classes that deal with files and any input/output channels:

  • java.nio.ByteBuffer/CharBuffer/DoubleBuffer/LongBuffer/ShortBuffer
  • java.nio.channels.SelectionKey
  • java.nio.channels.Selector
  • java.nio.file.Path
  • java.nio.file.Files

The has a new method, void writeBytes(byte[]);, which writes all the bytes of the parameter to the output stream at once. and

The class has two new constructors and has four new constructors that allow a Charset to be specified.

Each of the classes has a new method, respectively InputStreamnullInputStream(), OutputStreamnullOutputStream(), ReadernullReader(), and WriternullWriter(); that reads or writes no bytes.

When you first look at these methods, you might wonder what they are used for? You can think of them like /dev/null for throwing away output you don’t need or providing an input that always returns zero bytes.


The java.nio.ByteBuffer/CharBuffer/DoubleBuffer/LongBuffer/ShortBuffer classes now have a mismatch() method that finds and returns the relative index of the first mismatch between this buffer and a given buffer.


The java.nio.channels.SelectionKey class has new two methods, intinterestOpsAnd(int) and intinterestOpsOr(int);. The first method sets atomically this key’s interest set to the bitwise intersection (“and”) of the existing interest set and the given value, while the second method atomically sets this key’s interest set to the bitwise union (“or”) of the existing interest set and the given value.


The java.nio.channels.Selector class has three methods:

  • int select(Consumer, long); selects and performs an action on the keys whose corresponding channels are ready for I/O operations with a long parameter representing the timeout
  • intselect(Consumer) behaves like the previous method without the timeout
  • intselectNow(Consumer) behaves like the second method, except it is non-blocking

The java.nio.file.Path class has two new methods for constructing a path:

  • The first is Path of(String, String[]); which returns a Path by converting a path string or a sequence of strings that when joined form a string path
  • The second method, Path of(URI);, returns a Path by converting a URI

The java.nio.file.Files class contains a collection of static utility methods to work with files and paths. New in Java 11 is the straightforward String readString(Path) method. You pass in a path and get back the string contents of the file. If you use this method, you don’t have to worry about opening and closing file streams or converting the path of a file to a string because it’s all done for you.

This particular method assumes that the file is encoded using UTF-8. If you know the file is encoded using a different character set, you can use the overloaded method String readString(Path, Charset), which allows you to pass in the appropriate character set.

Conversely, there’s also a PathwriteString(Path, CharSequence, OpenOption[]) method. The first parameter is the path of the file that you want to write to. Then you can pass in the character sequence in a string as a character sequence. And lastly, you can specify options that instruct writeString how to open the file; for example, you can append or you can overwrite.

It’s up to you to define. Like with readString, there’s also an overloaded method Path writeString(Path, CharSequence, Charset, OpenOption[]) that takes an explicit character set if you don’t want to use the UTF-8 default.

Streams and Optional classes

The methods I’ll highlight in this category are:

  • java.util.Optional/OptionalInt/OptionalDouble/OptionalLong
  • java.util.function.Predicate

The java.util.Optional/OptionalInt/OptionalDouble/OptionalLong classes all have a new method called booleanisEmpty();. If a value is not present, it returns true, otherwise it returns false.

You already have the optional isPresent() method, but now you also have the isEmpty counterpart as well. This is how you would use it:

jshell>var optional = Optional.ofNullable(null)
optional ==>Optional.empty

$1 ==> true

$2 ==> false

Here, you’re creating an empty optional using Optional.ofNullable() and passing in null. You could also call the optional empty, but in both cases, optional is an empty optional. Calling isEmpty on optional will return true and isPresent will return false.


The last new method that’s introduced in Java 11 that I want to show you is the Predicatenot(Predicate) method on java.util.function.Predicate. This is one of my favorite new APIs in JDK 11.

The predicate is a functional interface that describes a single method that takes an object and returns a Boolean. For example, whenever you pass a lambda to the filter method on a stream, that’s a predicate.

As an example, you can convert this code:

jshell> "\nOne\nTwo\nThree\nFour\n   "
.filter( item -> !item.isBlank())


"\nOne\nTwo\nThree\nFour\n   "

and if we use a static import it becomes:

"\nOne\nTwo\nThree\nFour\n   "

Personally, I think this version is more readable and easier to understand.

Utility classes

The utility classes I’d like to highlight include:

  • java.util.regex.Pattern
  • java.util.Collection
  • java.util.concurrent.TimeUnit
  • jdk.jshell.EvalException

The java.util.regex.Pattern class has a new hidden gem of a method called Predicate asMatchPredicate(); which creates a predicate that tests if this pattern matches a given input string.


The class java.util.Collection has a new method Object[] toArray(IntFunction); that returns an array containing all of the elements in this collection using the provided generator function to allocate the returned array:

jshell>var y = List.of("One","Two","Three").toArray(String[]::new)
y ==>String[3] { "One", "Two", "Three" }

The class java.util.concurrent.TimeUnit has a new method called long convert(java.time.Duration); that converts the given time duration to this unit.


Finally, the class jdk.jshell.EvalException has a new JShellExceptiongetCause(); method that returns a wrapped cause of the throwable in the executing client represented by this EvalException or null if the cause is non-existent or unknown.

Changes to the modules and the SecurityManager class are the important takeaways in Java 11.

The modules

There are six new classes and eight new methods in the modules you will want to familiarize yourself with, and you’ll find them in the JDK Enhancement Proposals JEP 324 and JEP 329.

JEP 324 addresses implementing key agreement using Curve25519 and Curve448, a “key agreement scheme that is more efficient and secure than the existing elliptic curve Diffie-Hellman (ECDH) scheme”.

JEP 329 addresses implementing the ChaCha20 and ChaCha20-Poly1305 stream ciphers to replace the older, insecure RC4 stream cipher.

The SecurityManager class

Four methods in the SecurityManager class have been removed in Java 11:

  • checkAwtEventQueueAccess
  • checkSystemClipboardAccess
  • checkTopLevelWindow
  • checkMemberAccess

You can guess from these method names that they had dependencies on the Abstract Window Toolkit (AWT), a desktop technology. The SecurityManager class, however, is a core API of Java.

Now you can imagine that it’s not a good idea to have a dependency from a core API to a higher-level desktop API. This caused quite a problem when modularizing the JDK in Java 9. Since handing the core Java base module dependency to the desktop module wasn’t an option, this dependency had to be cut.

The way this was done in Java 9 was to deprecate these methods and implement the checks using more generic permissions. To do this meant that the semantics of these methods also changed, which was another reason why they had to go.

These very specific check methods should be replaced with calls to the generic checkPermission(Permission) method, where you, as a user of the API, pass in the appropriate permission that needs to be checked. This can be an AWT permission or any other permission. In this manner, the core SecurityManager API is no longer entangled with desktop APIs.

Now, these aren’t all the new APIs and methods, but it is a good sample of the more important features in this category.

Language features

Now it’s time to look beyond the libraries and see what changed in the language in Java 11. In this section, you will explore:

  • var for lambda parameters
  • How to launch single-file source code programs
  • The new, standardized HTTP Client
  • The enhanced switch statement and new switch expression syntax

var for lambda parameters

There’s one small language change in Java that builds upon the local variable type inference that was introduced in Java 10 (JEP 286). As a quick refresher, what you can do as of Java 10 is to replace the type of a local variablewith var.

jshell>var name = “Mohamed Taman”

The type of the variable name will still be a string, but instead of having to type it yourself, it will be inferred by the compiler based on the right-hand side of the assignment.

But what about lambdas? JEP 323 extends the use of this syntax to the parameters of Lambda expressions. This is an example of a lambda expression:
.map((var s) ->s.toLowerCase())

Of course, an astute Java programmer would point out that using var is superfluous here since Lambda expressions already have type inference and you could just as easily rewrite the same code as:
.map(s ->s.toLowerCase())

But why would you want to use var in this case when you can also completely leave off the types and get the same type inference behavior? The one compelling argument for this is that you can annotate types in Java and in this very special case, var is also treated as a sort of type:
.map((@Notnullvar s) ->s.toLowerCase())

Now you can put in @Nonnull or @Nullable annotation on variable s and they can be used to check additional guarantees on your code, like the nullability of a variable. You can’t use these annotations without a type. So, by using var in lambda parameter declarations, you can get type inference and leave off the explicit type. Plus, you gain the ability to use annotations.

There are some limitations to using var in lambdas. They come down to the fact that you have to be uniform in the uses of var.

One, you cannot use var on a parameter and then leave it off on the next parameter:

(var x, y) ->x.concat(y)

Two, you also can’t mix var with explicit types:

(var x, String y) ->x.concat(y)

Three, there’s also the short annotation for lambdas where you can leave off the parentheses if there’s just a single parameter:


In this short annotation, the use of var is also disallowed:


Thanks to this small language change in Java SE 11, these three examples will all fail to compile.

Even if you’re not planning to use this change yourself, it’s good to know about it in case you see it in code reviews or in other places.

How to launch single-file source code programs

To write an application that just prints “Hello World!” requires you to write a class with a public static void main method and use the System.out.println method. By doing this, you must then compile the code using javac. And finally, you can run the application to be welcomed to the world using a java interpreter command line.

Doing the same thing in most scripting languages is significantly simpler and quicker and this syntax verbosity has been a criticism of Java, one of the reasons it is a more difficult language to approach for beginners.

This iteration eliminates the need to compile a single-file source-code application (JEP 330). Now you can type:


and it allows you to execute a Java source code file directly using the Java interpreter. The source code is compiled in memory and then executed by the interpreter directly.

For detailed information about this new feature, explore the tutorial “Java theory and practice: Run single-file source code programs without compilation” (IBM Developer, April 2019).

The new, standardized HTTP Client

In JDK 9, a new API was introduced that provides support for the HTTP Client protocol. It was introduced as an incubator module as a part of the Java Platform Module System (JPMS). You can read more about it in the JDK Enhancement Proposal 110.

Incubator modules are intended to provide new APIs but without making them a part of the Java SE standard. This way, developers can try the API and provide feedback and once any necessary changes have been, the API can be moved to become part of the standard.

JEP 321 introduces the HTTP 1.1/2 and WebSocketClient APIs as integral parts of the Java SE 11 standard. This brings a new module and packages to the JDK,, which contains the following API main types:

  • HttpClient
  • HttpRequest
  • HttpResponse
  • WebSocket

The API can be used for synchronous or asynchronous calls based on your business case. Asynchronous mode makes use of CompletableFutures and CompletionStages.

For detailed information about this new feature, explore the tutorial “Java theory and practice: Explore the new Java SE 11 HTTP Client and WebSocket APIs” (IBM Developer, April 2019).

The enhanced switch statement and new switch expression syntax

JEP 354 makes an interesting proposition. It would extend the switch statement (in JDK 12) so that it can be used as either a statement or an expression, and both forms could use either a “traditional” or “simplified” scoping and control flow behavior. These changes will simplify everyday coding.

Control flow
Control flow is the order in which individual statements, functions, and instructions of a program are executed or evaluated. The switch statement allows the value of a variable or expression to change the control flow of program execution or evaluation via search and map.

I’ll borrow examples from JEP 354 to show you the new syntax and how the code becomes cleaner and more precise. For example, the following code has many unnecessary verbose break statements and this visual noise often makes it hard to debug errors. And if you miss including a break statement, that means that accidental fall-through occurs and bugs are created.

switch (day) {
case MONDAY:
case FRIDAY:
case SUNDAY:

With the new switch statement, the previous code can now be re-written as:

switch (day) {
case MONDAY, FRIDAY, SUNDAY ->System.out.println(6);
case TUESDAY                ->System.out.println(7);
case THURSDAY, SATURDAY     ->System.out.println(8);
case WEDNESDAY              ->System.out.println(9);

Most of your uses of switch statements are essentially trying to simulate switch expressions, in which each case either assigns to a common target variable or returns a value as the following:

switch (day) {
case MONDAY:
case FRIDAY:
case SUNDAY:
numLetters = 6;
numLetters = 7;
numLetters = 8;
numLetters = 9;
throw new IllegalStateException("Wat: " + day);

It should be possible to say that directly, using the new switch expression, which is both clearer and safer:

intnumLetters = switch (day) {
case TUESDAY                -> 7;
case THURSDAY, SATURDAY     -> 8;
case WEDNESDAY              -> 9;

For detailed information about this new feature, explore the tutorial “Java theory and practice: Effective use of the new switch statement and expression in Java SE 12” (IBM Developer, April 2019).

Under the hood features

Now let’s take a look at a few of the behind-the-scenes features in Java 11, including:

  • Nest-based access control
  • Flight Recorder
  • Transport Layer Security 1.3(TLS)
  • Unicode 10
  • Unicode 11

Nest-based access control

Java supports nested classes through the inner class concept. In this example, the compiler does its magic to make this work:

public class Outer {
private intouterInt;
class Inner {
public void printOuterInt() {
System.out.println("Outer int = " + outerInt);

The compiler modifies this code to create something like this before compiling the code:

public class Outer {
private intouterInt;
public int access$000() {
return outerInt;

class Inner$Outer {
Outer outer;
public void printOuterInt() {
System.out.println("Outer int = " + outer.access$000());

Although logically the inner class is part of the same code entity as the outer class, it is compiled as a separate class, therefore it requires a synthetic bridge method to be created by the compiler to provide access to the private field of the outer class.

JEP 181 introduces the concept of nests in which two members of the same nest (the outer and inner in my example) are nest mates. Two new attributes are defined for the class file format:

  • NestHost
  • NestMembers

These changes are useful for other languages that support nested classes and are compiled to bytecodes. This feature introduces three new methods to java.lang.Class:

  • Class getNestHost()
  • Class[] getNestMembers()
  • booleanisNestmateOf(Class)

Flight Recorder

Prior to JDK 11, Flight Recorder was a commercial feature in the Oracle JDK binary. Flight Recorder is a low-overhead, data-collection framework for the JVM.

Oracle is eliminating the functional differences between the Oracle JDK and one built from OpenJDK source code and this feature has been contributed to the OpenJDK through JEP 328. It contains two new modules — jdk.jfr and — to provide the following:

  • APIs for producing and consuming data as events
  • A buffer mechanism and a binary data format
  • Allow the configuration and filtering of events
  • Events for the OS, the HotSpot JVM, and the JDK libraries

Transport Layer Security 1.3 (TLS)

JEP 332 introduces support for the TLS 1.3, a major overhaul of the TLS protocol that provides significant security and performance improvements over previous versions. The JDK now supports this, although this does not extend to Datagram Transport Layer Security (DTLS).

Unicode 10

In Java 11, JEP 327 proposes to upgrade existing platform APIs to support Unicode 10. The previous release of Java supported Unicode 8. Unicode 10 contains more than 16,000 new characters, like the following:

jshell>var bitcoin = "\u20BF"
bitcoin ==> "&8383;"

Yes, you are right, this is official Bitcoin sign, now part of Unicode and Java. I know you’ve always wanted to use the Bitcoin Unicode character in Java and now you can finally do so in Java 11.

Unicode 11

To continue supporting languages and providing better-localized applications, JDK 12 will include support for Unicode 11, which introduces 684 new characters, 11 new blocks, and 7 new scripts:

  • The 684 new characters include important additions for 66 emoji characters, copyleft symbol, half stars for rating systems, additional astrological symbols, and Xiangqi Chinese chess symbols.
  • The 7 new scripts are Hanifi Rohingya, Old Sogdian, Sogdian, Dogra, GunjalaGondi, Makasar, and Medefaidrin.
  • The 11 new blocks include 7 blocks for the new scripts listed in the previous bullet and 4 blocks for the following existing scripts: GeorgianExtended, MayanNumerals, IndicSiyaqNumbers, and ChessSymbols.

Bid farewell to these modules and methods

We will remember these modules, methods, and tools fondly, but hopefully, we will enjoy their more efficient descendants the next time we code.

With the introduction of JPMS in JDK 9, the monolithic rt.jar file has been divided into multiple modules. An additional advantage of JPMS is it is now possible to create a Java runtime that only includes the modules you need for your application, reducing the runtime size considerably. With cleanly defined module boundaries, it is now simpler to remove parts of the Java API that are outdated.

However, the six modules containing Enterprise APIs under were deprecated for removal in Java 9. Now with Java 11, the time has actually come to remove these modules and APIs from the JDK and this is what this JEP 320 does.

Now, any applications with dependencies on these APIs that are in any of these modules will not compile and run on Java 11, at least not without making changes.

The affected modules are:

  • corba
  • transaction
  • activation
  • xml.bind

Let’s look at JAXB because it’s used in many popular frameworks, for example Spring. If you use Spring outside of a Java EE container, you will now have to add a dependency to your application on JAXB and a JAXB implementation yourself.

Fortunately, for most of the technologies libraries that are removed from the JDK, the APIs and suitable implementations are available and maintained separately (for example, on the Maven central repository). That way, you can add them easily to your project or continue to use them when you migrate.

In the case of JAXB, you’ll have to add this dependency to your application so you have the JAXB API to compile and run against. Besides the APIs definitions, you’ll also need an implementation of JAXB.

What happened to JavaFX?

Now, JavaFX has not been deprecated, but there are changes in the way it’s distributed. JavaFX is the latest UI technology in Java that was part of the Oracle JDK 8, 9, and 10. If you downloaded and used Oracle JDK 8, 9, or 10, then you could be using JavaFX APIs. If you were using OpenJDK 8, 9, or 10, then you had to add JavaFX as dependencies to your application.

As of Java 11, Oracle JDK also no longer bundles JavaFX. This makes Oracle JDK 11 and OpenJDK 11 equivalent in this regard. It doesn’t mean that JavaFX isn’t a viable technology any longer, it has just been moved out of the JDK into the OpenJFX project, an open source sub-project that runs the development of JavaFX. This means that JavaFX can now develop and innovate and release at its own pace, no longer tied to the JDK’s schedule.

For developers, it does mean that if you develop JavaFX applications, you can no longer depend on JavaFX being in the Oracle JDK. Fortunately, the JavaFX JARs are being published on Maven Central with the release of Java 11. If you develop JavaFX applications, you need to add this dependency to your application.

I’m afraid I’ll have to ask you methods to leave

Also in Java 11, some individual methods were removed as well.


The destroy() and stop(Throwable) methods have been removed from the class java.lang.Thread. The stop() method that takes no arguments is still present. This might present a compatibility issue.

The countStackFrames method has been marked as deprecated for removal, whereas the remaining deprecated methods are merely marked deprecated and not deprecated for removal.

java.lang.System and java.lang.Runtime

Two methods with the same name — runFinalizersOnExit() — but in different classes (java.lang.System and java.lang.Runtime) are gone. and (in JDK 12)

The finalize() methods of and will be removed. They were deprecated for removal in JDK 9.

The java.lang.ref.Cleaner was implemented in JDK 9 as the primary mechanism to close file descriptors that are no longer reachable from FileInputStream and FileOutputStream. The recommendation is to explicitly call close() or use try-with-resources to close files. and the Java Security Standard Algorithm Names specification (in JDK 12)

TLS v1 and v1.1-related algorithms will be removed from the API and the Java Security Standard Algorithm Names specification in JDK 12.

com.sun.awt.SecurityWarning (in JDK 12)

The com.sun.awt.SecurityWarning class was deprecated forRemoval=true in JDK 11. This class was unused in the JDK and has been removed in this release.

Tools deprecated

You can also say goodbye to some tools.

Deprecate the Nashorn Scripting Engine

JEP 335 marked the Nashorn JavaScript Engine as deprecated. It was introduced in JDK 8 as a higher-performing replacement to the Rhino JavaScript Engine. The intention is to remove Nashorn, along with the associated APIs and jjs tool from a future version of Java. When this happens has not been decided. The possibility of using the GraalVM as a replacement has been suggested, but how that will work has not been evaluated.

Deprecate the Pack200 Tools and APIs

Pack200 is a compression scheme for JAR files, introduced in Java SE 5.0. With the introduction of the JPMS in JDK 9, Pack200 is no longer used to compress the JDK itself. The pack200 and unpack200 tools, as well as the Pack200 API in the Java util.jar, are now deprecated and may be removed in a future version of the JDK. When this happens has not been specified. Read all about it in JEP 336.


Starting with Java SE 11 and for the first time in Java history, you can execute a script containing Java code directly without compilation. The Java 11 source execution feature makes it possible to write scripts in Java and execute them directly from the command line.

JDK 11 is the latest LTS release of the JDK (as defined by Oracle and being followed by everyone else). Although there are not a ton of developer-focused features, there’s a lot going on further down in the JVM, possibly laying the groundwork for future, more prominent features.

Some of the other tutorials in this series will deal with many of these changes in more detail. Check them out, and get ahead on the next iteration of Java coding.