The six-month release cadence of the JDK continues to deliver the incremental evolution of Java. The latest version is JDK 13, which is released in September 2019.
Get the Java version in AdoptOpenJDK (HotSpot JVM):
$ java -version openjdk version "13" 2019-09-17 OpenJDK Runtime Environment AdoptOpenJDK (build 13+33) OpenJDK 64-Bit Server VM AdoptOpenJDK (build 13+33, mixed mode, sharing)
This article will summarize several JEP released in JDK 13.
JEP 350: Dynamic CDS Archives
JEP 350 extend application class-data sharing (AppCDS) to allow the dynamic archiving of classes at the end of Java application execution. The archived classes will include all loaded application classes and library classes that are not present in the default, base-layer CDS archive.
Using AppCDS prior to this was a multi-step process involving the creation of a list of relevant classes and using this list to generate the archive to be used for subsequent runs. Now, all that is required is a single run of an application with the -XX:ArchiveClassesAtExit flag providing the location where the archive will be written. Use -XX:SharedArchiveFile To run the same application using this dynamic archive.
JEP 351: ZGC - Uncommit Unused Memory
ZGC is introduced in JDK 11, does not currently uncommit and return memory to the operating system, even when that memory has been unused for a long time. This behavior is not optimal for all types of applications and environments, especially those where memory footprint is a concern. JEP 351 aims to enhance ZGC to return unused heap memory to the operating system.
JEP 353: Reimplement the Legacy Socket API
JEP 353 replace the underlying implementation used by the java.net.Socket and java.net.ServerSocket APIs with a simpler and more modern implementation that is easy to maintain and debug. java.net.ServerSocket API implementations date back to JDK 1.0. The implementation of these APIs uses several techniques (such as using the thread stack as the IO buffer) that make them inflexible and hard to maintain. JDK 13 provides a new implementation, NioSocketImpl, which no longer requires native code, so simplifies porting to multiple platforms. The new implementation will be easy to adapt to work with user-mode threads, a.k.a. fibers, currently being explored in Project Loom.
Let's check following Socket example:
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class JEP353 {
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(8888)) {
boolean running = true;
serverSocket.setSoTimeout(5000); // timeout in 5 seconds
Socket clientSocket = serverSocket.accept();
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
while (running) {
out.println("output");
if (out.checkError()) {
System.out.println("ERROR writing data to socket !!!");
}
System.out.println(clientSocket.isConnected());
System.out.println(clientSocket.getInputStream().read());
// thread sleep ...
// break condition , close sockets and the like ...
}
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
It's compiled with option --enable-preview, and when we run it:
$ java --enable-preview JEP353 Accept timed out
And when we run it with showing debug (info) output - with findstr (filtering) "Socket":
$ java --enable-preview -Xlog:class+load=info JEP353 | findstr Socket [0.053s][info][class,load] java.net.ServerSocket source: jrt:/java.base [0.053s][info][class,load] jdk.internal.access.JavaNetSocketAccess source: jrt:/java.base [0.053s][info][class,load] java.net.ServerSocket$1 source: jrt:/java.base [0.053s][info][class,load] java.net.SocketOptions source: jrt:/java.base [0.053s][info][class,load] java.net.SocketImpl source: jrt:/java.base [0.058s][info][class,load] java.net.SocketImpl$$Lambda$1/0x0000000800bb0840 source: java.net.SocketImpl [0.062s][info][class,load] sun.net.PlatformSocketImpl source: jrt:/java.base [0.062s][info][class,load] sun.nio.ch.NioSocketImpl source: jrt:/java.base [0.063s][info][class,load] sun.nio.ch.SocketDispatcher source: jrt:/java.base [0.072s][info][class,load] java.net.SocketAddress source: jrt:/java.base [0.072s][info][class,load] java.net.InetSocketAddress source: jrt:/java.base [0.072s][info][class,load] java.net.InetSocketAddress$InetSocketAddressHolder source: jrt:/java.base [0.074s][info][class,load] sun.net.ext.ExtendedSocketOptions source: jrt:/java.base [0.074s][info][class,load] jdk.net.ExtendedSocketOptions source: jrt:/jdk.net [0.074s][info][class,load] java.net.SocketOption source: jrt:/java.base [0.074s][info][class,load] jdk.net.ExtendedSocketOptions$ExtSocketOption source: jrt:/jdk.net [0.074s][info][class,load] jdk.net.SocketFlow source: jrt:/jdk.net [0.075s][info][class,load] jdk.net.ExtendedSocketOptions$PlatformSocketOptions source: jrt:/jdk.net [0.075s][info][class,load] jdk.net.ExtendedSocketOptions$PlatformSocketOptions$1 source: jrt:/jdk.net [0.075s][info][class,load] jdk.net.ExtendedSocketOptions$1 source: jrt:/jdk.net [0.075s][info][class,load] sun.nio.ch.NioSocketImpl$FileDescriptorCloser source: jrt:/java.base [0.076s][info][class,load] java.net.Socket source: jrt:/java.base [5.078s][info][class,load] java.net.SocketTimeoutException source: jrt:/java.base [5.078s][info][class,load] java.net.StandardSocketOptions source: jrt:/java.base [5.078s][info][class,load] java.net.StandardSocketOptions$StdSocketOption source: jrt:/java.base [5.079s][info][class,load] sun.nio.ch.SocketOptionRegistry source: jrt:/java.base [5.079s][info][class,load] sun.nio.ch.SocketOptionRegistry$RegistryKey source: jrt:/java.base [5.079s][info][class,load] sun.nio.ch.SocketOptionRegistry$LazyInitialization source: jrt:/java.base [5.079s][info][class,load] sun.nio.ch.ExtendedSocketOption source: jrt:/java.base [5.079s][info][class,load] sun.nio.ch.ExtendedSocketOption$1 source: jrt:/java.base [5.080s][info][class,load] java.net.SocketException source: jrt:/java.base
To switch back to PlainSocketImpl by using Djdk.net.usePlainSocketImpl setting:
$ java --enable-preview -Xlog:class+load=info -Djdk.net.usePlainSocketImpl JEP353 | findStr Socket [0.051s][info][class,load] java.net.ServerSocket source: jrt:/java.base [0.051s][info][class,load] jdk.internal.access.JavaNetSocketAccess source: jrt:/java.base [0.051s][info][class,load] java.net.ServerSocket$1 source: jrt:/java.base [0.052s][info][class,load] java.net.SocketOptions source: jrt:/java.base [0.052s][info][class,load] java.net.SocketImpl source: jrt:/java.base [0.057s][info][class,load] java.net.SocketImpl$$Lambda$1/0x0000000800bb0840 source: java.net.SocketImpl [0.068s][info][class,load] sun.net.PlatformSocketImpl source: jrt:/java.base [0.068s][info][class,load] java.net.AbstractPlainSocketImpl source: jrt:/java.base [0.068s][info][class,load] java.net.PlainSocketImpl source: jrt:/java.base [0.069s][info][class,load] java.net.AbstractPlainSocketImpl$1 source: jrt:/java.base [0.083s][info][class,load] sun.net.ext.ExtendedSocketOptions source: jrt:/java.base [0.083s][info][class,load] jdk.net.ExtendedSocketOptions source: jrt:/jdk.net [0.083s][info][class,load] java.net.SocketOption source: jrt:/java.base [0.083s][info][class,load] jdk.net.ExtendedSocketOptions$ExtSocketOption source: jrt:/jdk.net [0.083s][info][class,load] jdk.net.SocketFlow source: jrt:/jdk.net [0.084s][info][class,load] jdk.net.ExtendedSocketOptions$PlatformSocketOptions source: jrt:/jdk.net [0.084s][info][class,load] jdk.net.ExtendedSocketOptions$PlatformSocketOptions$1 source: jrt:/jdk.net [0.084s][info][class,load] jdk.net.ExtendedSocketOptions$1 source: jrt:/jdk.net [0.084s][info][class,load] java.net.StandardSocketOptions source: jrt:/java.base [0.084s][info][class,load] java.net.StandardSocketOptions$StdSocketOption source: jrt:/java.base [0.089s][info][class,load] sun.net.ext.ExtendedSocketOptions$$Lambda$2/0x0000000800bb1040 source: sun.net.ext.ExtendedSocketOptions [0.095s][info][class,load] java.net.SocketAddress source: jrt:/java.base [0.095s][info][class,load] java.net.InetSocketAddress source: jrt:/java.base [0.099s][info][class,load] java.net.InetSocketAddress$InetSocketAddressHolder source: jrt:/java.base [0.100s][info][class,load] java.net.SocketCleanable source: jrt:/java.base [0.101s][info][class,load] java.net.Socket source: jrt:/java.base [5.101s][info][class,load] java.net.SocketTimeoutException source: jrt:/java.base
JEP 354: Switch Expressions (Preview)
Before Java 12, a switch could only be used as a statement, used to perform action(s) but did not return a result. In JDK 12, a switch can now be used as an expression, returns a result that can be assigned to a variable - together with several changes to the syntax of case statements within the switch. Please check Java 12 - Switch Expressions (JEP 325).
JDK 13's JEP 354 extend switch so it can be used as either a statement or an expression, and so that both forms can use either traditional case ... : labels (with fall through) or new case ... -> labels (with no fall through), with a further new statement for yielding a value from a switch expression. These changes will simplify everyday coding, and prepare the way for the use of pattern matching (JEP 305) in switch.
import java.time.LocalDate;
public class JEP354 {
public static void main(String[] args) {
LocalDate now = LocalDate.now();
final int day = now.getDayOfWeek().getValue();
// traditional switch
switch (day) {
case 2:
case 3:
case 4:
case 5:
case 6:
System.out.println("weekday");
break;
case 7:
case 1:
System.out.println("weekend");
break;
default:
System.out.println("invalid");
}
// case L -> syntax
// no break necessary, only code next to -> runs
switch (day) {
case 2, 3, 4, 5, 6 -> System.out.println("weekday");
case 7, 1 -> System.out.println("weekend");
default -> System.out.println("invalid");
}
// switch expression
// then switch should be exhaustive if used as expression
// using yield for yielding a value from a switch expression
final String attr = switch (day) {
case 2, 3, 4, 5, 6: yield "weekday";
case 7, 1: yield "weekend";
default: yield "invalid";
};
System.out.println(attr);
}
}
Until JDK 13, this is also still a preview feature. More in Java 13 - Switch Expressions (JEP 354)
JEP 355: Text Blocks (Preview)
JEP 355 aims to add text blocks to the Java language. A text block is a multi-line string literal that avoids the need for most escape sequences, automatically formats the string in a predictable way, and gives the developer control over format when desired. This is a preview language feature in JDK 13.
public class JEP355 {
public static void main(String[] args) {
// normal String concat
String html1 = "<html>\n" +
" <body>\n" +
" <p>Hello, <strong>concatenated string</strong></p>\n" +
" </body>\n" +
"</html>\n";
System.out.println(html1);
// Using text blocks
String html2 = """
<html>
<body>
<p>Hello, <strong>text blocks</strong></p>
</body>
</html>
""";
System.out.println(html2);
// normal String concat
String sql1 = "SELECT STREET_NAME, BUILDING_NAME \n" +
"FROM TBL_POSTAL_CODE \n" +
"WHERE POSTAL_CODE = ?\n";
System.out.println(sql1);
// Using text blocks
String sql2 = """
SELECT STREET_NAME, BUILDING_NAME
FROM TBL_POSTAL_CODE
WHERE POSTAL_CODE = ?
""";
System.out.println(sql2);
}
}
And when we run it:
<html> <body> <p>Hello, <strong>concatenated string</strong></p> </body> </html> <html> <body> <p>Hello, <strong>text blocks</strong></p> </body> </html> SELECT STREET_NAME, BUILDING_NAME FROM TBL_POSTAL_CODE WHERE POSTAL_CODE = ? SELECT STREET_NAME, BUILDING_NAME FROM TBL_POSTAL_CODE WHERE POSTAL_CODE = ?
Download JDK 13
JDK 13 already available in for download in AdoptOpenJDK page or for Oracle's JDK version in Oracle's download page.