Java is officially announced the release date of version 14 on March 17, 2020. We’re gonna look at Java 14 major features with examples.
- Install Java 14
- Switch Expressions (Standard)
- Pattern Matching for instanceof (Preview)
- JFR Event Streaming
- Helpful NullPointerExceptions
- Records (Preview)
- Text Blocks (Second Preview)
Install Java 14
You can follow these steps to setup Java 14 on your machine:
Switch Expressions (Standard)
Friendlier and less error-prone, Switch Expressions were a ‘preview’ feature in earlier releases. Java 14 make it standard now.
You typically write a switch like this:
switch (type) {
case RUN, START:
System.out.println("RUN");
break;
case STOP:
System.out.println("STOP");
break;
default:
System.out.println("Unknown");
}
All the break
statements ensures that the next block in the switch statement is not executed.
Now we can refactor the code to make use of this new switch form:
switch (type) {
case RUN, START -> System.out.println("RUN");
case STOP -> System.out.println("STOP");
default -> System.out.println("Unknown");
};
With Switch Expression:
System.out.println(
switch (type) {
case RUN, START -> "RUN";
case STOP -> "STOP";
default -> "Unknown";
}
);
Java 14 introduces new yield
statement to yield a value which becomes the value of the enclosing Switch Expression. For example:
String state = switch (type) {
case RUN, START -> "RUN";
case STOP -> "STOP";
default -> {
System.out.println("Not recognize state!");
yield "Unknown";
}
}
);
Or you can also use traditional switch block like this:
String state = switch (type) {
case RUN, START:
yield "RUN";
case STOP:
yield "STOP";
default -> {
System.out.println("Not regconize state!");
yield "Unknown";
}
}
);
Pattern Matching for instanceof (Preview)
Pattern matching make conditional extraction of components from objects more concise and safe.
Before Java 14:
if (obj instanceof Tutorial) {
Tutorial t = (Tutorial) obj;
if (t.getId() == 2020) {
return t.getTitle();
}
}
You can see that we have to do 3 things:
- make a test (is obj a
Tutorial
?) - do a conversion (casting obj to
Tutorial
) - declare a new local variable
t
In some cases, we also need to do more check such as t.getId() == 2020
.
Java 14 Enhancement:
if (obj instanceof Tutorial t && t.getId() == 2020) {
return t.getTitle();
} else {
// ...
}
If obj
is an instance of Tutorial
, it is cast to Tutorial
and assigned to t
variable. The binding variable t
is in scope on the right hand side of the &&
operator (only evaluated if instanceof
succeeded and assigned to t
).
Notice that the pattern will only match (and t
will only be assigned) if obj
is NOT null.
JFR Event Streaming
Java Flight Recorder (JFR) collects diagnostic and make data profiling about a running Java application.
JFR Event Streaming exposes the Flight Recorder data for continuous monitoring, both for in-process and out-of-process applications. It also helps to record the same set of events as in the non-streaming case.
Without JFR Event Streaming, here are what a user should do for consuming the data:
- start a recording
- stop it
- dump the contents to disk
- parse the recording file
jdk.jfr
module has package jdk.jfr.consumer
that provides functionality to subscribe to events asynchronously. We can read recording data directly, or stream, from the disk repository without dumping a recording file.
The jdk.jfr.consumer.EventStream
interface provides way to filter and consume events regardless if the source is a live stream or a file on disk.
public interface EventStream extends AutoCloseable {
public static EventStream openRepository();
public static EventStream openRepository(Path directory);
public static EventStream openFile(Path file);
void setStartTime(Instant startTime);
void setEndTime(Instant endTime);
void setOrdered(boolean ordered);
void setReuse(boolean reuse);
void onEvent(Consumer<RecordedEvent> handler);
void onEvent(String eventName, Consumer<RecordedEvent> handler);
void onFlush(Runnable handler);
void onClose(Runnable handler);
void onError(Runnable handler);
void remove(Object handler);
void start();
void startAsync();
void awaitTermination();
void awaitTermination(Duration duration);
void close();
}
You can see 3 factory methods to create a stream:
openRepository(Path directory)
constructs anEventStream
from a disk repository. It helps to monitor other processes by working directly against the file system.openRepository()
performs in-process monitoring. This method does not start a recording. Instead, the stream receives events only when recordings are started by external (using JCMD or JMX).- openFile(Path file) creates an
EventStream
from a recording file.
We work with an EventStream
by registering a handler which will be invoked in response to the arrival of an event.
The following example prints the overall CPU usage and locks contended for more than 10 ms. We’re gonna use RecordingStream
class, an implementation of EventStream
interface.
try (var rs = new RecordingStream()) {
rs.enable("jdk.CPULoad").withPeriod(Duration.ofSeconds(1));
rs.enable("jdk.JavaMonitorEnter").withThreshold(Duration.ofMillis(10));
rs.onEvent("jdk.CPULoad", event -> {
System.out.println(event.getFloat("machineTotal"));
});
rs.onEvent("jdk.JavaMonitorEnter", event -> {
System.out.println(event.getClass("monitorClass"));
});
rs.start();
}
Helpful NullPointerExceptions
Assume that tutorial
is a null object, so for the code:
tut.id = 2020;
We’re gonna get a NullPointerException
(NPE):
Exception in thread "main" java.lang.NullPointerException
at Prog.main(Prog.java:8)
What if the following code throws a NPE:
blog.tutorial.author.name = "bezkoder";
We don’t know exactly which variable was null. Was it blog
or tutorial
or author
?
Now look at the following cases which also throw NPE:
// a or a[i] or a[i][j]?
arr[i][j][k] = 42;
// a or b?
a.i = b.j;
// x() or y() return null?
x().y().i = 42;
Java 14 improves the usability of NullPointerException
to reduce the confusion and concern that new developers often have about it.
To enable code details in exception messages, we can run with command-line option: -XX:+ShowCodeDetailsInExceptionMessages
.
JVM will generate messages like this:
tut.id = 2020;
Exception in thread "main" java.lang.NullPointerException:
Cannot assign field "id" because "tut" is null
at Prog.main(Prog.java:8)
blog.tutorial.author.name = "bezkoder";
Exception in thread "main" java.lang.NullPointerException:
Cannot read field "author" because "blog.tutorial" is null
at Prog.main(Prog.java:8)
arr[i][j][k] = 42;
Exception in thread "main" java.lang.NullPointerException:
Cannot load from object array because "arr[i][j]" is null
at Prog.main(Prog.java:8)
a.i = b.j;
Exception in thread "main" java.lang.NullPointerException:
Cannot read field "j" because "b" is null
at Prog.main(Prog.java:8)
In the future, this feature might be enabled default.
Records (Preview)
Records help us to write domain classes with only storing data in fields purpose without any custom behaviors.
For example, we have a Tutorial
class with 3 fields: id, title, description.
What we need to do is to declare:
- constructor
- Getter methods
toString()
hashCode()
&equals()
public class Tutorial {
private int id;
private String title;
private String author;
public Tutorial(int id, String title, String author) {
this.id = id;
this.title = title;
this.author = author;
}
public int getId() {
return id;
}
public String getTitle() {
return title;
}
public String getAuthor() {
return author;
}
@Override
public String toString() {
return "Tutorial [id=" + id + ", title=" + title + ", author=" + author + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((author == null) ? 0 : author.hashCode());
result = prime * result + id;
result = prime * result + ((title == null) ? 0 : title.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Tutorial other = (Tutorial) obj;
if (author == null) {
if (other.author != null)
return false;
} else if (!author.equals(other.author))
return false;
if (id != other.id)
return false;
if (title == null) {
if (other.title != null)
return false;
} else if (!title.equals(other.title))
return false;
return true;
}
}
Using a record
, we can make the implementations of constructor, getters, hashCode()
, equals()
and toString()
automatically.
public record Tutorial(int id,
String title,
String author) { }
The Tutorial record will be compiled like this:
final class Tutorial extends java.lang.Record {
private final int id;
private final java.lang.String title;
private final java.lang.String author;
public Tutorial(int id, java.lang.String title, java.lang.String author) { /* compiled code */ }
public java.lang.String toString() { /* compiled code */ }
public final int hashCode() { /* compiled code */ }
public final boolean equals(java.lang.Object o) { /* compiled code */ }
public int id() { /* compiled code */ }
public java.lang.String title() { /* compiled code */ }
public java.lang.String author() { /* compiled code */ }
}
Note:: We need to compile the file using the preview flag command:
javac --enable-preview --release 14 Tutorial.java
Text Blocks (Second Preview)
Text Blocks was introduced in Java 13 as a preview feature and continue to be the second round of preview with Java 14. It helps us to work with multiline string literals.
We sometimes write code with many string concatenations and escape sequences for multiline text formatting.
For example:
String html = "<html>" +
"\n\t" + "<body>" +
"\n\t\t" + "<h1>\"Be zKoder!\"</h1>" +
"\n\t" + "</body>" +
"\n" + "</html>";
Using Java 14 Text Blocks, we can write easy to read code with 3 quotation marks at the beginning and end of a text block as following:
String html = """
<html>
<body>
<h1>"Java 14 is here!"</h1>
</body>
</html>""";
We can also use a backslash \
for a long line to split up for nice-looking text block.
String text = """
Lorem ipsum dolor sit amet, consectetur \
adipiscing elit, sed do eiusmod tempor incididunt \
ut labore et dolore magna aliqua.\
""";
Conclusion
Java 14 provides features and updates to help us.
- Switch Expressions become standard.
- Pattern Matching
instanceof
reduces explicit casts. - JDK Flight Recorder provides Event Streaming for continuous monitoring.
NullPointerException
for better debugging & diagnostics- Records declare classes for storing data purpose
- Text blocks helps to work with multiline string values and supports new escape sequences.
Happy Learning! See you again.
Thanks!
Hello there! This tutorial couldn’t be written any better! Going through this article reminds me of my previous roommate! He always kept talking about this. I most certainly will send this article to him. Fairly certain he’s going to have a very good read. Thanks for sharing!
Thank a lot! There are many java 14 tutorials but this one is clear with examples.