java 14 new features

Java 14 – What’s New Features In Java 14 | With Example Configuration

Java 14 is now available!

Since Java 9, the version delivered so quickly (every 6 months) that it is difficult to follow all the functionalities provided by each version… But don’t worry. I have gathered all that concerns Java 14 for you in this post!

Java 14 has been released since March 17, 2020, and on the program, heavy, very big stuff: Switch expressions, Pattern matching, text blocks, records, clearer NPEs, the multiplatform packaging tool, and much more! For each novelty, I have summarized what seemed to me the most important but you will also have the link to the corresponding EHD if you want to satisfy your curiosity ?

So, Let’s start!


That’s it, after a Preview (Java 12) and a Second Preview (Java 13), it took 2 versions to finally standardize the switch expressions.

Concretely, there is no change in this functionality between version 13 and 14 of Java (except the fact that it is no longer necessary to activate the preview mode).

However, here’s a booster shot:

Before we used switch expressions in a very verbose way, where the break loses its meaning and the boxes are repeated line by line:

var  weekPartType;
switch (day) {
    case MON:
    case TUE:
    case WED:
    case THU:
    case FRI:
         weekPartType = WORK_WEEK;
    case SAT:
    case SUN:
        weekPartType = WEEK_END;
        throw  new  UnsupportedOperationException("Unknown day: " + day);

Now we can write it like lambdas:

var  weekPartType = switch (day) {
    case MON, TUE, WED, THU, FRI -> WORK_WEEK;
    case SAT, SUN -> {
        yield WEEK_END;
    default  ->  throw  new  UnsupportedOperationException("Unknown day: " + day);

Each block of code in a switch expression must imperatively end with a yield, which therefore replaces the break.


We all saw an NPE (NullPointerException) come out of nowhere, and complicated to clarify, with the magnificent full message as it should be: null!

For example, with an NPE triggered on a.b (). C (). D () = 15, which is null? at ? b ()? vs() ? The only way to detect the source of the null is to analyze it all in debug mode …

But that era is over! By adding the -XX: + ShowCodeDetailsInExceptionMessages parameter at runtime, you will see your error indicating exactly the source of the problem in the form Cannot invoke “a.b ()” because “a” is null. And this is valid whether for method calls, assignment of fields, tables, etc …

Benefit: You know exactly where the problem is coming from.

Disadvantage: The error message will contain the name of the fields or methods associated with the error, which could potentially compromise the security of your code as these details will be valid for all your NPEs and not for a specific class. So think carefully before using this new feature!


In general, to follow the life of an application, either we connect a monitoring tool, such as New Relic, or we profile the application with JProfiler or VisualVM for example. . In the first case, we must determine in advance the elements we want to monitor and place probes, in the second, it is a rather long process which gives us trends but for which it is often difficult to reproduce the actual production conditions.

Java 14 is now accompanied by the JDK Flight Recording Event Streaming which allows you to have as much or more information than profiling, continuously and without waiting for the end of the collection!

The only problem is the CPU load which can get heavy when there are a lot of events observed.

Now we just have to wait for the usual monitoring tools to implement this new API … To be continued!

In the meantime, here is an article for those wishing to dig a little deeper into the subject and better understand its use (in English): Java Flight Recorder and JFR Event Streaming in Java 14.


All the features in Preview and Second Preview will only be accessible if you activate Preview mode, here is an article.


Here is another long-awaited feature that makes devs like me dream of writing multi-line novels in Java String or SQL for example!

The text blocks were already in Java 13 in Preview, but got a second round to make a meaningful change: the addition of \ and \ s escapes.

But hey, nothing beats a booster shot (already two in this article!):

Before, to make a single line in multiline, we had to pay attention to the space at each end of a concatenated String:

@Query("SELECT e " +
    "FROM Employee e " +
    "WHERE e.organizationId = 42")
List<Employee> listEmployeesOnOrganization42();

And now, no need to worry about concatenation anymore:

    SELECT e \
    FROM Employee e \
    WHERE e.organizationId = 42""")
List<Employee> listEmployeesOnOrganization42();

Another example with line breaks …

Before, you had to escape special characters (like line breaks in the example):

String  xmlConfig = "<root>\\n" +
                    "  <var1>value</var1>\\n" +
                    "  <var2>value</var2>\\n" +

Now the line breaks are implied, and the indentation is “guessed” based on the line that starts furthest to the left:

String  xmlConfig = """

Explanations and summary:

  • By default, each new line in a block of text will indicate a line break with \ n but without keeping spaces at the end of each line (as if the end of lines were “trim”)
  • The escape character \ at the end of a block line indicates that the line will not end with a line break (exactly the same operation as with command lines!) While keeping spaces at the end of lines of the block (example with a line \ where the 3 spaces at the end will be kept)
  • The escape character \ s at the end of a block line indicates that the line will end with a space and a line break while keeping spaces at the end of the lines of the block (example with here • a • line •• \ s where the 2 spaces at the end will be kept, with a 3rd space and a line break. The final String will therefore contain here: • a • line ••• \ n)
  • Indentation inside the text block will be “guessed” based on the line that starts furthest to the left
  • There is no need to escape special characters, such as \ n newlines or quotes “

Isn’t life good?


Simple but powerful feature, it allows declaring a local variable directly in the declaration of an instance of condition, which reduces the code and clarifies it.

Before, we needed to declare the value cast in a local variable, quite verbose:

if (obj instanceof String) {
    String  s = (String) obj;
    if (s.length() > 3) {
        // use of 's'

Or to cast directly into the condition, which makes the code much more complex to read:

if (obj instanceof String && ((String)obj).length() > 3) {
    // If we want to use obj as a String, we will have to either assign it or cast it each time ...

Now we can directly cast and create a local variable corresponding to the instance, all in one line:

if (obj instanceof  String s && s.length() > 3) {
    // Using 's', we gained 2 lines!


A new feature that will be very useful to avoid all the redundant code (getters, setters, equals/hashcode/constructor / toString…).

The power of this novelty – or rather this new language element – is that it will greatly facilitate the writing of simple POJOs and will allow us to have a cleaner code with a description of our objects reduced to the essential.

Come on, let’s start with an example!

Before, we had:

class  EnginQuiRoule {
    private  final  boolean  outOrder;
    private  final  int  nbRoues;
    EnginQuiRoule(boolean  outOrder, int  nbRoues) {
        this.outOrder= outOrder;
        this.nbRoues = nbRoues;
    boolean  isOutOrder() { return  this.outOrder; }
    int  getNbRoues(int  nbRoues) { return  this.nbRoues; }
    // toString, equals, hashcode...
static  void  main(String... args) {
    var  voitureOutOrder = new  EnginQuiRoule(true, 4);
    boolean  estOutOrder = voitureEnPanne.isOutOrder();
    int  nombreDeRoues = voitureEnPanne.getNbRoues();

Then we could use lombok to avoid all the redundant code:

class  EnginQuiRoule {
    boolean  outOrder;
    int  nbRoues;
static  void  main(String... args) {
    var  voitureOutOrder = new  EnginQuiRoule(true, 4);
    boolean  estOutOrder = voitureEnPanne.isOutOrder();
    int  nombreDeRoues = voitureEnPanne.getNbRoues();

And now with the records, we have:

record EnginQuiRoule(boolean outOrder, int nbRoues) {}
static  void  main(String... args) {
    var  voitureOutOrder = new  EnginQuiRoule(true, 4);
    boolean  estOutOrder = voitureEnPanne.outOrder();
    int  nombreDeRoues = voitureEnPanne.nbRoues();

A mini example with reactive programming :

record LightResponse(String url, int responseStatus, String responseContent) {}
// ...
    .flatMap(url ->  call(url).map(resp ->  new  LightResponse(url, resp.status, resp.content)))
    .filter(response ->  response.status() != 404)

Of course, if you want to add behavior, just complete the body of the record, for example:

record LightResponse(Integer responseStatus, String responseContent) {
    public  LightResponse(Integer responseStatus, String responseContent) {
        if (responseStatus == null) throw  new  IllegalArgumentException("Pas bien !");
        // No need to do this.url = url; because it is implicit!

Here are the few rules of a record:

  • The generated accessors are in fluent programming mode, so there is no prefix (usually get and is)
  • you can implement an interface, but not extend a class.
  • toString () returns each field and their value (example: MyClass [field1 = X, field2 = Y])
  • the equals / hashCode is well based on all fields

Personally, I see this functionality as a revolution when doing reactive programming with Reactor, RxJava or Java Streams.

Indeed, when we manipulate our objects in a stream, we can very easily end up with a Tuple whose content is difficult to know without having read all the code.

With records, we can quickly type the objects and give explicit names to the objects handled while having a minimalist code.


Finally, I will list other new features here that will not be described, with a few links if you want to dig deeper into the subject:

  • NUMA-Aware Memory Allocation for G1 (JEP345): Improved performance of G1-GC on machines with a lot of memory by implementing NUMA-aware memory allocation (memory management halfway between SMP and clustering ).
  • Non-Volatile Mapped Byte Buffers (JEP352)
  • [Experimental] Port of ZGC on macOS (JEP364) and Windows (JEP365)
  • [Incubator] Foreign-Memory Access API (JEP370): Currently we have Unsafe which is very widely used to optimize memory access, whether for introspection or network. The goal of this novelty would be to optimize the current direct ByteBuffer and to do without the Unsafe as much as possible. Which is not an API officially supported by Java, and which can easily “break” an application for problems of memory corruption.
  • [Incubator] Packaging Tool (JEP343): The new tool to generate a CLI installer for any OS (deb / rpm for Linux, pkg / dmg for MacOS, and exe / msi for Windows)
  • Pack200 Tools and API history tool removed (JEP367): Used a long time ago to optimize jar transfer. If you still need it, you can turn to jlink
  • Concurrent Mark Sweep (CMS) Garbage Collector Removal (JEP363)

With the release of Java 14 and these many new features, Which can be used with or without preview mode. The Java community is asserting its desire to make the Java language a modern language and resolutely turned towards the future.

In this article, I told you about the most important news (in my eyes). However, if you want to go further, I suggest you check out the Java 14 release notes!

For any questions or comments relating to this article, feel free to leave a message below.

Thank you for reading this post 🙂

Sources :

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *