Skip to content

Java 13 Features (with Examples)

Cup of cappuccino with a heart in the milk foam on a wooden table – Java 13 Features

Java 13 was released on September 17, 2019.

The changes in Java 13 are modest. In total, only five JDK Enhancement Proposals (JEPs) have made it into the release – and three of them are classified as experimental or preview features.

The article starts with the experimental and preview features, as these are the most exciting changes in Java 13.

Next are performance improvements, enhancements to the JDK class library, and other changes that we rarely encounter in our daily development work.

Experimental and Preview Features

I will not go into the experimental and preview features in full depth here. You can find a detailed description in those parts of the series where these features reached production maturity.

Switch Expressions (Second Preview)

JEP 325 introduced switch expressions as a preview in Java 12. switch can be used as a statement or as an expression since then. “Expression” means that switch returns a value, such as in the following example, which is based on the one in the JEP:

int numLetters = switch (day) {
  case MONDAY, FRIDAY, SUNDAY:
    break 6;
  case TUESDAY:
    break 7;
  case THURSDAY, SATURDAY:
    break 8;
  case WEDNESDAY:
    break 9;
};

Based on feedback from the developer community, JDK Enhancement Proposal 354 replaces the break keyword in switch expressions with the new yield keyword:

int numLetters = switch (day) {
  case MONDAY, FRIDAY, SUNDAY:
    yield 6;
  case TUESDAY:
    yield 7;
  case THURSDAY, SATURDAY:
    yield 8;
  case WEDNESDAY:
    yield 9;
};

yield is a so-called restricted identifier: Variables may still have that name, but you must qualify calls to a method named yield – e.g., Thread.yield() or this.yield(…). Java 13 emits a warning for unqualified calls; as of Java 14, they are a compile error. And as of Java 14, a class can no longer be named yield.

Switch expressions reached production status in the next release, Java 14. You can find all details about them in the main article on switch expressions.

Text Blocks (Preview)

To define a multiline string before Java 13, we had to use escape sequences for line breaks and double quotes contained in the string. An SQL statement looked like this, for example:

String sql =
    "SELECT id, firstName, lastName FROM Employee\n"
        + "WHERE departmentId = \"IT\"\n"
        + "ORDER BY lastName, firstName";

JDK Enhancement Proposal 355 allows us to write such a string in a much more readable way:

String sql = """
    SELECT id, firstName, lastName FROM Employee
    WHERE departmentId = "IT"
    ORDER BY lastName, firstName""";

In Java 13 and 14, you must enable text blocks as a preview feature – either in your IDE (in IntelliJ via File→Project Structure→Project Settings→Project→Project language level) or with the --enable-preview parameter when running the javac and java commands.

Together with text blocks, three new methods were added to the String class – in Java 13 also as preview APIs:

  • stripIndent() removes the common indentation from all lines – using the same algorithm the compiler applies to text blocks.
  • translateEscapes() translates escape sequences such as \n and \t in a string.
  • formatted(Object... args) is equivalent to String.format(this, args) and makes inserting values into a text block more readable.

After a second preview in Java 14, text blocks reached production readiness in Java 15 – and so did the three String methods. You can find an introduction in all details in the main article on text blocks.

ZGC: Uncommit Unused Memory (Experimental)

ZGC is an experimental garbage collector introduced in Java 11 that promises extremely short stop-the-world pauses of 10 ms or less (since Java 16, the goal is less than one millisecond).

JDK Enhancement Proposal 351 extends ZGC to return unused heap memory to the operating system after a specific time.

Using -XX:ZUncommitDelay, you can specify the time in seconds, after which ZGC returns unused memory. By default, this value is 300 seconds.

The feature is enabled by default and can be disabled with -XX:-ZUncommit. Since ZGC never shrinks the heap below the minimum heap size -Xms, the feature is effectively disabled if you set -Xms and -Xmx to the same value.

ZGC reached production status in Java 15; in the corresponding article, I introduce the garbage collector in more detail.

Performance Improvements

Dynamic CDS Archives

Java 10 introduced Application Class-Data Sharing – a feature that allows creating a so-called shared archive file. This file contains the application classes in a binary form as required by the JVM of the platform used. The file is mapped into the JVM’s memory via memory-mapped I/O.

Before Java 13, it was laborious to create this file. First, we had to dump a class list during a test run of the application. Only in a second step could we generate the shared archive from this list.

The following sample java calls are taken from the article linked above:

java -Xshare:off -XX:+UseAppCDS \
    -XX:DumpLoadedClassList=helloworld.lst \
    -cp target/helloworld.jar eu.happycoders.appcds.Main

java -Xshare:dump -XX:+UseAppCDS \
    -XX:SharedClassListFile=helloworld.lst \
    -XX:SharedArchiveFile=helloworld.jsa \
    -cp target/helloworld.jar

JDK Enhancement Proposal 350 simplifies this process. As of Java 13, you can specify the -XX:ArchiveClassesAtExit parameter to generate the shared archive at the end of the application execution:

java -XX:ArchiveClassesAtExit=helloworld.jsa \
    -cp target/helloworld.jar eu.happycoders.appcds.Main

The -XX:+UseAppCDS option from the Java 10 example is missing here for a reason: The JVM has not known it since Java 12, because AppCDS has been enabled by default since Java 11. A -Xshare:on is unnecessary as well, since -Xshare:auto is the default.

The created shared archive is much smaller than before (256 KB instead of 9 MB). It now only contains the classes of the application. The JDK classes are loaded from the base archive classes.jsa delivered with the JDK.

The shared archive is used as follows as of Java 13:

java -XX:SharedArchiveFile=helloworld.jsa \
    -cp target/helloworld.jar eu.happycoders.appcds.Main

In the article linked at the beginning of this section, you can find an example of using AppCDS with step-by-step instructions. Try to reproduce the example and use the new -XX:ArchiveClassesAtExit option instead of the previous two steps.

Since Java 19, -XX:+AutoCreateSharedArchive creates and uses the archive in a single invocation, and since Java 24, the AOT cache builds on CDS and additionally stores the classes in loaded and linked form.

Soft Max Heap Size

You can use the new command line parameter -XX:SoftMaxHeapSize to set a “soft” upper limit for the heap size. The garbage collector will then try to keep the heap below this limit and only exceed it if necessary to avoid an OutOfMemoryError.

The application scenario lies in environments in which you pay for the actual RAM usage. Thus, the heap can generally be kept small but may temporarily grow beyond the soft upper limit when memory requirements increase.

The flag is declared as “manageable”, so you can change it at runtime – e.g., with jcmd <pid> VM.set_flag SoftMaxHeapSize <bytes> or via the HotSpot MXBean. This lets you raise or lower the limit without restarting the application.

In Java 13, only the (still experimental) ZGC supports this flag. Since Java 16, Shenandoah supports it as well; G1 still does not (as of Java 27).

There is no JEP for this change; it is tracked in the bug tracker under JDK-8222145 (the flag) and JDK-8222182 (the ZGC support).

JDK Class Library Enhancements

The ByteBuffer class has received several methods that allow read/write operations to be performed at specified buffer positions instead of the position managed by the ByteBuffer, as was previously the case. In addition, there are new variants of FileSystems.newFileSystem() and factory methods for XML parsers with namespace support.

If you need a refresher on ByteBuffer, I recommend this ByteBuffer basics article.

ByteBuffer.slice()

Using ByteBuffer.slice() you can create a view on a section of the buffer. This method, which already existed before Java 13, returns a view that starts at the buffer’s current position and whose capacity and limit correspond to the remaining bytes in the buffer.

New is the method ByteBuffer.slice(int index, int length). It allows you to create a view that starts at position index and contains length bytes. The new method ignores the buffer’s position; however, the range must lie within the limit, otherwise it throws an IndexOutOfBoundsException.

There is no JEP for this change; it is tracked in the bug tracker under JDK-5071718.

New ByteBuffer.get() and put() Methods

Analogously, there are two new get() and two new put() methods, which do not read/write the data at the current position of the buffer – but at an explicitly specified position:

  • get(int index, byte[] dst, int offset, int length) – transfers length bytes from the buffer position specified by index into the byte array dst starting at position offset.
  • get(int index, byte[] dst) – transfers data from the buffer position specified by index into the byte array dst. The number of bytes transferred is equal to the length of the destination array.
  • put(int index, byte[] src, int offset, int length) – transfers length bytes from the byte array src starting at position offset into the buffer starting at position index.
  • put(int index, byte[] src) – transfers all bytes from the byte array src into the buffer starting at position index.

The buffer’s position remains unchanged for all four methods.

There is no JEP for this change; it is tracked in the bug tracker under JDK-5029431.

FileSystems.newFileSystem()

Using the FileSystems.newFileSystem(Path path, ClassLoader loader) method, you can create a pseudo-file system with contents mapped to a file (such as a ZIP or JAR file).

The method was overloaded in Java 13 with a variant that makes it possible to pass a provider-specific file system configuration: FileSystems.newFileSystem(Path path, Map env, ClassLoader loader).

Furthermore, two variants have been added, each without the loader parameter. A class loader is only required if the so-called FileSystemProvider for the file type to be mapped is not registered in the JDK but must be loaded via the specified class loader. For standard file types like ZIP or JAR, this is not required.

A migration pitfall: If you call the old two-parameter variant with null as the class loader – FileSystems.newFileSystem(path, null) – the call is ambiguous as of Java 13 and no longer compiles. A cast fixes it: FileSystems.newFileSystem(path, (ClassLoader) null).

There is no JEP for this change; it is tracked in the bug tracker under JDK-8218875.

XML Parsers with Namespace Support

The factories DocumentBuilderFactory and SAXParserFactory each received three new methods that create parsers with namespace support enabled: newDefaultNSInstance(), newNSInstance(), and newNSInstance(String factoryClassName, ClassLoader classLoader). Previously, you had to call setNamespaceAware(true) after creating the factory:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newDefaultInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();

As of Java 13, one line is enough:

DocumentBuilder db =
    DocumentBuilderFactory.newDefaultNSInstance().newDocumentBuilder();

There is no JEP for this change; it is tracked in the bug tracker under JDK-8219692.

Other Changes in Java 13 (Which You Don’t Necessarily Need to Know About as a Java Developer)

This section lists changes that rather few Java developers will come into contact with.

Reimplement the Legacy Socket API

The java.net.Socket and java.net.ServerSocket APIs have existed since Java 1.0, and the underlying code (a mix of Java and C code) is difficult to maintain and extend, especially in light of Project Loom, in which virtual threads (lightweight threads managed by the JVM) were developed – final since Java 21.

JDK Enhancement Proposal 353 replaces the old implementation with a more modern, better maintainable, and extensible implementation, which in particular can be adapted to Project Loom without further refactorings.

Unicode 12.1

As in the previous two Java releases, Unicode support has been increased in Java 13 – to version 12.1. That means that classes such as String and Character must handle the new characters, blocks, and scripts.

For an example, see the article about Unicode 10 in Java 11.

There is no JEP for this change; it is tracked in the bug tracker under JDK-8221431.

Minor Changes Without a JEP

  • The JapaneseEra class knows the “Reiwa” era, which began on May 1, 2019 – as the constant JapaneseEra.REIWA or via JapaneseEra.of(3) (JDK-8205432).
  • The maximum heap size of ZGC has been increased from 4 TB to 16 TB (JDK-8221786).

Complete List of All Changes in Java 13

This article has presented all the features of Java 13 that are defined in JDK Enhancement Proposals – and enhancements to the JDK class library that are not associated with any JEP.

For a complete list of changes, see the official Java 13 Release Notes.

Summary

Java 13 is a modest release.

In the second preview of “Switch Expressions”, break was replaced by yield. Multiline strings finally made their way into the language with the “Text Blocks” preview.

The experimental ZGC can return unused memory to the operating system and may be configured with a “soft” maximum heap size.

“Dynamic CDS Archives” makes employing Application Class-Data Sharing a piece of cake from Java 13 onwards.

ByteBuffer has been extended with methods to read and write at absolute positions, there are some new variants of the FileSystems.newFileSystem() method, and XML factories can directly create parsers with namespace support.

The Java 1.0 Socket API has been completely rewritten to be fit for the lightweight threads developed in Project Loom – the virtual threads that have been a permanent part of the Java platform since Java 21.

Did this article answer your questions? Then I’d be happy about a review on my ProvenExpert profile – it helps other developers find this content.

👉 Leave a review

Java evolves faster than you might think – a new release ships every six months. To make sure you don’t miss a feature, click here and sign up for the HappyCoders newsletter.

👉 Newsletter Sign-up

This subject in your own code?

You have read the article – in the training your team works with it. Over 2 days we go through the subjects on your own projects instead of constructed examples.

Hands-on, easy to understand, and directly applicable to your day-to-day project work. Instead of theory, I teach principles that help you write code that is better, more maintainable, and more performant in the long run.

Java 17 TrainingSee all trainings

Become a Better Java Developer

My free newsletter keeps you ahead. Modern Java: new versions & features, performance, and JVM insights – once a month.

Search