JDK 11 — The Second LTS
Released in September 2018, JDK 11 is the second LTS (Long-Term Support) release after JDK 8. It is the most commonly chosen version when migrating from JDK 8 to modern Java, and many enterprise environments use it as their production baseline.
JDK 11 includes all features added in JDK 9 and 10 — such as the module system and var — while adding practical improvements like the official HTTP Client API and enhanced String/Files utilities.
HTTP Client API
The HTTP Client, first introduced as an incubator module in JDK 9, became a standard API (java.net.http) in JDK 11. It replaces the complex, low-level HttpURLConnection API and natively supports HTTP/2, asynchronous requests, and WebSocket.
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class HttpClientExample {
public static void main(String[] args) throws Exception {
// Create HttpClient — HTTP/2 by default, with timeout configuration
var client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.connectTimeout(Duration.ofSeconds(10))
.build();
// Create a GET request
var request = HttpRequest.newBuilder()
.uri(URI.create("https://jsonplaceholder.typicode.com/posts/1"))
.header("Accept", "application/json")
.GET()
.build();
// Synchronous request: send() — blocks until the response arrives
HttpResponse<String> response = client.send(
request,
HttpResponse.BodyHandlers.ofString()
);
System.out.println("Status code: " + response.statusCode());
System.out.println("Protocol: " + response.version());
System.out.println("Body length: " + response.body().length() + " chars");
// Output:
// Status code: 200
// Protocol: HTTP_2
// Body length: 292 chars
// Asynchronous request: sendAsync() — returns a CompletableFuture
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(body -> System.out.println("Async response received: " + body.length() + " chars"))
.join(); // Wait for completion on the main thread
// Output: Async response received: 292 chars
// POST request
var postRequest = HttpRequest.newBuilder()
.uri(URI.create("https://jsonplaceholder.typicode.com/posts"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(
"{\"title\": \"Test\", \"body\": \"Content\", \"userId\": 1}"
))
.build();
var postResponse = client.send(postRequest, HttpResponse.BodyHandlers.ofString());
System.out.println("POST status code: " + postResponse.statusCode());
// Output: POST status code: 201
}
}
HttpClient is thread-safe, so a single instance can be shared across the entire application. There is no need to create a new one for each request.
New String Methods
JDK 11 added utility methods to the String class that are frequently needed in everyday development.
import java.util.stream.Collectors;
public class StringMethods {
public static void main(String[] args) {
// isBlank(): check if the string is empty or contains only whitespace
System.out.println(" ".isBlank()); // true
System.out.println(" ".isEmpty()); // false — contains whitespace characters
System.out.println("hello".isBlank()); // false
// isBlank() treats whitespace, tabs, and newlines as "blank", unlike isEmpty()
// strip(): remove leading and trailing whitespace (Unicode-aware)
String text = " \u2000Hello Java\u2000 ";
System.out.println("trim: [" + text.trim() + "]");
// Output: trim: [ Hello Java ] — does not handle Unicode whitespace
System.out.println("strip: [" + text.strip() + "]");
// Output: strip: [Hello Java] — removes Unicode whitespace as well
System.out.println("stripLeading: [" + text.stripLeading() + "]");
// Output: stripLeading: [Hello Java ]
System.out.println("stripTrailing: [" + text.stripTrailing() + "]");
// Output: stripTrailing: [ Hello Java]
// lines(): convert a string into a Stream of lines
String multiline = "First line\nSecond line\nThird line";
long lineCount = multiline.lines().count();
System.out.println("Line count: " + lineCount);
// Output: Line count: 3
// Filtering blank lines example
String withBlanks = "Hello\n\n \nWorld\n";
var nonBlank = withBlanks.lines()
.filter(line -> !line.isBlank())
.collect(Collectors.toList());
System.out.println("Non-blank lines: " + nonBlank);
// Output: Non-blank lines: [Hello, World]
// repeat(): repeat a string
String separator = "-".repeat(30);
System.out.println(separator);
// Output: ------------------------------
System.out.println("*".repeat(5));
// Output: *****
}
}
The difference between strip() and trim() lies in Unicode whitespace handling. trim() only removes ASCII whitespace (code point 32 and below), while strip() removes all characters for which Character.isWhitespace() returns true. In multilingual environments, strip() is the safer choice.
Files Utility Methods
Reading or writing an entire file as a string is now possible in a single line.
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public class FilesUtility {
public static void main(String[] args) throws IOException {
// Create a temporary file
Path tempFile = Files.createTempFile("jdk11-demo", ".txt");
// writeString(): write a string directly to a file
Files.writeString(tempFile, "File writing got simpler in JDK 11.\nThis is the second line.");
// readString(): read the entire file as a string
String content = Files.readString(tempFile);
System.out.println("File content:");
System.out.println(content);
// Output:
// File content:
// File writing got simpler in JDK 11.
// This is the second line.
System.out.println("File size: " + Files.size(tempFile) + " bytes");
// Output: File size: 59 bytes
// Cleanup: delete the temporary file
Files.delete(tempFile);
System.out.println("Temporary file deleted");
// Output: Temporary file deleted
}
}
Tasks that previously required reading line by line with BufferedReader and assembling with StringBuilder, or using external libraries (Apache Commons IO, Guava), can now be done in a single line with the standard API.
Using var in Lambdas
The var keyword introduced in JDK 10 can now be used for lambda parameters in JDK 11 (JEP 323). The reason for explicitly using var when type inference is already possible is to enable annotations on lambda parameters.
import java.util.List;
import java.util.stream.Collectors;
public class LambdaVar {
// Annotation definition (example)
@interface NonNull {}
public static void main(String[] args) {
var names = List.of("Java", "Kotlin", "Scala", "Groovy");
// Before JDK 10: explicit type or omitted
var result1 = names.stream()
.filter(name -> name.length() > 4)
.collect(Collectors.toList());
// JDK 11: use var to enable annotations on lambda parameters
var result2 = names.stream()
.filter((@NonNull var name) -> name.length() > 4)
.collect(Collectors.toList());
System.out.println("Longer than 5 chars: " + result2);
// Output: Longer than 5 chars: [Kotlin, Scala, Groovy]
}
}
Running Source Files Directly with java
Starting with JDK 11, .java source files can be run directly without compilation (JEP 330). This lets you skip the javac step when running simple scripts or test code.
# Before JDK 10: compile then run (2 steps)
javac Hello.java
java Hello
# JDK 11: run source file directly (1 step)
java Hello.java
This only works for single source files. For projects consisting of multiple files, you still need to use javac. On Unix/Linux, adding a shebang (#!/usr/bin/java --source 11) allows the file to be run like a shell script.
Performance-Related Changes
ZGC (experimental): ZGC (JEP 333) was experimentally introduced in JDK 11. It is an ultra-low-latency GC designed to keep Stop-the-World pauses below 10ms. It guarantees short pause times even with terabyte-scale heaps. It later reached Production Ready status in JDK 15.
Epsilon GC: A “no-op” GC that does nothing. It only allocates memory without reclaiming it, and the JVM terminates when the heap is full. Its use cases include measuring GC overhead in performance benchmarks, or completely eliminating GC costs in short-lived applications (such as batch jobs).
Nest-Based Access Control: When inner classes accessed private members of their enclosing class, the compiler previously generated synthetic bridge methods. Starting with JDK 11, the JVM directly recognizes “nest” relationships, enabling direct access without bridge methods. This also works consistently with reflection.
Migration Checkpoints: JDK 8 to 11
Here are the key items to verify when upgrading from JDK 8 to 11.
| Item | What to Check |
|---|---|
| Module System | Determine if --add-opens or --add-modules options are needed |
| Removed APIs | Add separate dependencies for Java EE modules (javax.xml.bind, javax.annotation, etc.) |
| Internal APIs | Check for warnings or errors when using internal APIs like sun.misc.Unsafe |
| Build Tools | Update Maven/Gradle plugin versions |
| Libraries | Verify JDK 11 compatible versions of major frameworks (Spring, Hibernate, etc.) |
| GC Options | Remove PermGen-related options (-XX:PermSize) and replace with Metaspace options |
The javax to jakarta namespace change is particularly the biggest hurdle. javax.xml.bind (JAXB), javax.annotation, and others have been removed from the JDK, so they must be added as Maven/Gradle dependencies separately.
Summary
| Feature | Core Value |
|---|---|
| HTTP Client API | Modern HTTP communication with HTTP/2 and async support built in |
| String Methods | Simplified string handling with isBlank, strip, lines, repeat |
| Files Utility | Single-line file I/O with readString/writeString |
| Lambda var | Enables annotations on lambda parameters |
| Direct Source Execution | Run without compilation via java Hello.java |
| ZGC | Ultra-low-latency GC under 10ms (experimental) |
| Epsilon GC | No-op GC for benchmarks and short-lived applications |
JDK 11 is an LTS release that combines the stability of JDK 8 with the innovations of JDK 9 and 10. When migrating from JDK 8, start by checking module system compatibility and removed Java EE APIs, then actively adopt the HTTP Client API and new String methods. The next migration target would be JDK 17 (the third LTS) or JDK 21 (the fourth LTS, featuring Virtual Threads).