What's New in JDK 14

What’s New in JDK 14 and Beyond

Eric Solomon Development Technologies, Java Leave a Comment

Attention: The following article was published over 3 years ago, and the information provided may be aged or outdated. Please keep that in mind as you read the post.

JDK 14 was made Generally Available (GA) on 2020/03/17, bringing with it 16 new JDK Enhancement Proposals (JEPs). Coming 6 months after the release of JDK 13, it continues the JDK Project’s push for a strict, 6-month release cadence rather than the 2-year cadence it followed in the past. This faster cadence was initially proposed by Mark Reinhold as a means of progressing Java faster in a more consistent manner, as well as avoiding abnormally long delays that had plagued prior releases.

Why should one care about new JDK releases, though? As most Java developers are acutely aware, most of the industry is still dominated by JDK 8, so the likelihood of being able to use these new features in the near feature is probably low. However, as the industry gradually shifts from JDK 8 to the latest LTS release and becomes more accustomed to the new, 6-month release cadence, we should start seeing faster adoption rates of new releases. 

As a result, the need to continually sharpen your sword and stay up to date will increase. Also, if you are wanting to help kickstart the shift beyond JDK 8 in your projects, knowing what value later versions bring and being able to incorporate them into your proposal can be extremely helpful in making your goal of using a newer version a reality. Besides those two reasons, it is also just plain exciting to see what the latest and greatest is and where the language is heading.

So without further ado, let’s take a look at three of the more significant enhancements that come with JDK 14: Records, NullPointExceptions, and instanceof. If you like, you can check out the full list here. We’ll also briefly discuss JDK 15 and its promised JEPs.


Data carrier classes in Java have always suffered from being too verbose and requiring too much ceremony to implement. IDEs have helped mitigate this by generating the constructors, accessors, toString(), equals(), and hashCode(). This still ends up being a lot of boilerplate code that can quickly lead to defects. Records help alleviate this problem by reducing the boilerplate and allowing the user to actually model their data as data.

Basically, a record is an immutable, transparent data carrier for a fixed set of fields. Consider the following example.

record Point(int x, int y) { }

This record consists of a name (Point) and a state description (int x, int y) which declares the components of the record.

With a record, you get the following standard members automatically.

  • A private final field for each component of the state description
  • A public read accessor method for each component of the state description with the same name and type as the component
  • A public constructor whose signature is the same as the state description, which initializes each field from the corresponding argument
  • Implementations of equals and hashCode that say two records are equal if they are of the same type and contain the same state
  • An implementation of toString that includes the string representation of all the record components with their names

Pretty awesome, huh? At this point, you may be wondering if you can declare your own methods, fields, etc. in the body of a record. The short answer to this is yes, but do so with caution and intent. The goal of a record is to be a simple, shallow data carrier, so by adding more to it, if you’re not careful, you can undermine its simplicity and make it complicated.

One last cool thing to note about records is that you can define a constructor for validation or normalization of its parameters without a formal parameter list, as it is assumed to align with the state description.

record Point(int x, int y) { public Point { if (x < 0) { throw new IllegalArgumentException( "x must be >= 0"); } } } 

Helpful NullPointerExceptions

NullPointerExceptions arise when you attempt to use a reference to an object that has not yet been initialized. The error message generated is usually enough to figure out what the culprit is based on the line number, but not when you have multiple references on the same line or chained references. Consider the following example:

a.b.c.d = 1; 

Exception in thread "main" java.lang.NullPointerException
            at Example.main(Example.java:42)

Based on the error message, it is not immediately clear which object reference has resulted in the NullPointerException, as we are only given the line number to work with. With JEP 358 however, the error message will make it clearer as to the cause of the NullPointerException, as seen below.

Exception in thread "main" java.lang.NullPointerException:
       Cannot read field "c" because "b" is null
    at Example.main(Example.java:42)

Pattern Matching instanceof

Sometimes, you find yourself in a circumstance where you just don’t know what the exact type of an object is when you need to. To aide in times like this, Java provides you with the instanceof operator to help determine an object type.

Object o = "world";
if (o instanceof String) {
    String s = (String)o;
    System.out.println("hello " + s);

As you can see, this first requires you to explicitly cast the object before using it as that type. With JEP 305, the instanceof operator has been extended to allow a variable to be specified and implicitly cast to the type.

Object o = "world";
if (o instanceof String s) {
    System.out.println("hello " + s);

JDK 15

As of now, JDK 15 is scheduled to be GA on 2020/09/15. It has 5 JEPs targeted so far.

  • JEP 371: Hidden Classes – These are classes that cannot be used directly by the bytecode of other classes. The purpose of hidden classes is to be used by frameworks that generate classes at runtime and then to be used indirectly with reflection.
  • JEP 372: Remove the Nashorn Javascript Engine – This JEP would fully remove the Nashorn Javascript Engine, APIs, and jjs tool. This was previously deprecated back in JDK 11 with JEP 335.
  • JEP 377: ZGC – A Scalable, Low-Latency Garbage Collector that was introduced in JDK 11. This JEP would make the Z garbage collector to a production feature.
  • JEP 378: Text Blocks – First introduced in JDK 13 with JEP 355 and previewed for a second time in JDK 14 with JEP 368, this JEP proposes making text blocks a standard feature.
  • JEP 379: Shenandoah – A Low-Pause-Time Garbage Collector that was first integrated into JDK 12 with JEP 189. This JEP proposes making the GC a production feature.


JDK 14 has brought a multitude of new and exciting features that further enhance the language and overall developer experience. While we are still several months out, JDK 15 is shaping up to be quite good as well.

I look forward to seeing what the future holds for Java – now available in frequent, bite-sized chunks of goodness.

0 0 votes
Article Rating
Notify of

Inline Feedbacks
View all comments