Java 11 LTS Features
Java 11, released in September 2018, is the first long-term support (LTS) release after Java 8. It brings a solid mix of quality-of-life improvements to strings, files, lambdas, and HTTP — making everyday coding noticeably cleaner.
Note: Java 11 removed many deprecated APIs and the standalone JavaFX/Java EE modules. If you are migrating from Java 8, check for removed classes before upgrading.
New String Methods
Java 11 adds several helpful instance methods directly to String so you no longer need third-party helpers like Apache Commons or Guava for common tasks.
isBlank()
Returns true when the string is empty or contains only whitespace. Unlike isEmpty(), it understands Unicode whitespace.
public class StringIsBlank {
public static void main(String[] args) {
System.out.println("".isBlank()); // true
System.out.println(" ".isBlank()); // true
System.out.println(" hi ".isBlank()); // false
}
}
Output:
true
true
false
strip(), stripLeading(), stripTrailing()
These are the Unicode-aware replacements for trim(). The old trim() only removes ASCII control characters (≤ U+0020), while strip() respects all Unicode whitespace defined by Character.isWhitespace().
public class StringStrip {
public static void main(String[] args) {
String s = " Hello, Java 11! ";
System.out.println(s.strip()); // "Hello, Java 11!"
System.out.println(s.stripLeading()); // "Hello, Java 11! "
System.out.println(s.stripTrailing()); // " Hello, Java 11!"
}
}
Output:
Hello, Java 11!
Hello, Java 11!
Hello, Java 11!
repeat(int)
Repeats a string a given number of times.
public class StringRepeat {
public static void main(String[] args) {
System.out.println("ha".repeat(3)); // hahaha
System.out.println("-".repeat(20)); // --------------------
}
}
Output:
hahaha
--------------------
lines()
Splits a string into a Stream<String> of lines, respecting \n, \r, and \r\n line endings. Great for processing multi-line text without split("\n") edge cases.
import java.util.stream.Stream;
public class StringLines {
public static void main(String[] args) {
String text = "Line 1\nLine 2\nLine 3";
text.lines().forEach(System.out::println);
}
}
Output:
Line 1
Line 2
Line 3
Tip:
lines()is lazy — it returns aStreamrather than an eagerList, so it works efficiently even on large strings.
var in Lambda Parameters
Java 10 introduced var for local variable type inference. Java 11 extends this to lambda parameters, letting you annotate them with annotations while still relying on type inference.
import java.util.List;
import java.util.stream.Collectors;
public class VarInLambda {
public static void main(String[] args) {
var names = List.of("alice", "bob", "charlie");
// var lets you add annotations on lambda params (e.g., @Nonnull)
var upper = names.stream()
.map((@SuppressWarnings("unused") var name) -> name.toUpperCase())
.collect(Collectors.toList());
System.out.println(upper);
}
}
Output:
[ALICE, BOB, CHARLIE]
Note: All parameters in a lambda must use
var— you cannot mixvarwith explicit types or with the inferred (no-annotation) form within the same lambda.
New Files Utility Methods
java.nio.file.Files gains two new static methods that eliminate boilerplate when reading or writing whole files as strings.
import java.nio.file.Files;
import java.nio.file.Path;
public class FilesReadWrite {
public static void main(String[] args) throws Exception {
Path path = Path.of("hello.txt");
// Write a string directly to a file
Files.writeString(path, "Hello from Java 11!\nSecond line.");
// Read the whole file back as a String
String content = Files.readString(path);
System.out.println(content);
}
}
Output:
Hello from Java 11!
Second line.
Both methods use UTF-8 by default. An overload accepts a Charset if you need something else. These build on NIO.2 Path & Files introduced in Java 7.
The New HTTP Client (java.net.http)
The new HttpClient API — previewed in Java 9 and finalized in Java 11 — replaces the ancient HttpURLConnection. It supports HTTP/1.1 and HTTP/2, both synchronous and asynchronous requests, and WebSockets.
Synchronous GET
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class HttpClientDemo {
public static void main(String[] args) throws Exception {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/get"))
.GET()
.build();
HttpResponse<String> response =
client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("Status: " + response.statusCode());
System.out.println("Body: " + response.body().substring(0, 60) + "...");
}
}
Output:
Status: 200
Body: {
"args": {},
"headers": {
"Accept": "*/*",...
Asynchronous POST
import java.net.URI;
import java.net.http.*;
import java.util.concurrent.CompletableFuture;
public class HttpClientAsync {
public static void main(String[] args) throws Exception {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/post"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString("{\"key\":\"value\"}"))
.build();
CompletableFuture<HttpResponse<String>> future =
client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
future.thenAccept(resp -> System.out.println("Async status: " + resp.statusCode()))
.join(); // wait for demo purposes
}
}
Tip:
HttpClientinstances are thread-safe and intended to be reused across requests. Create one per application, not one per request.
Running Java Files Directly (Single-File Programs)
Java 11 lets you run a single .java source file with java without a separate javac compile step:
java HelloWorld.java
The JVM compiles and runs the file in memory. This is perfect for quick scripts and learning. The file must have only one class (or nested classes), and it cannot depend on other user-written .java files.
Note: The class inside the file does not need to match the file name for the single-file launcher (unlike normal compilation rules).
Nest-Based Access Control
Before Java 11, when an inner class accessed a private member of its outer class, the compiler quietly generated a package-private bridge method. This leaked implementation details and showed up in stack traces.
Java 11 introduces NestMates: the JVM understands the nest relationship directly, so inner classes can access each other’s private members without bridge methods.
public class Outer {
private String secret = "hidden";
class Inner {
void reveal() {
// Direct access — no bridge method in Java 11+
System.out.println("Secret: " + secret);
}
}
public static void main(String[] args) {
new Outer().new Inner().reveal();
}
}
Output:
Secret: hidden
You can inspect nest membership with the new Class methods: getNestHost() and getNestMembers().
Epsilon: No-Op Garbage Collector
Java 11 ships the Epsilon GC (-XX:+UseEpsilonGC) — a garbage collector that allocates memory but never collects it. When the heap is exhausted the JVM exits.
This sounds strange, but it is genuinely useful for:
- Performance testing (eliminate GC pauses from benchmarks)
- Short-lived batch jobs that fit in memory
- Testing that an application’s own memory management is correct
Warning: Never use Epsilon GC in a production server that handles unbounded work — your application will crash with
OutOfMemoryError.
ZGC (Experimental)
Java 11 also ships ZGC as an experimental low-latency collector (-XX:+UseZGC). It targets pause times under 10 ms regardless of heap size. ZGC became production-ready in Java 15 and gained further improvements in Java 21. See Garbage Collection Deep-Dive for the full picture.
Removed & Deprecated Features
Java 11 removed several long-deprecated APIs. The most impactful removals:
| Removed | Replacement |
|---|---|
java.xml.ws (JAX-WS) | Jakarta EE / standalone dependency |
java.xml.bind (JAXB) | jakarta.xml.bind artifact |
javafx.* modules | OpenJFX separate download |
java.se.ee meta-module | Individual Jakarta EE jars |
sun.misc.Unsafe.defineClass | MethodHandles.Lookup.defineClass |
Thread destroy() / stop(one-arg) | Cooperative interruption |
If you are migrating from Java 8, add the relevant Jakarta EE jars to your build tool (Maven/Gradle) to replace removed modules.
Under the Hood
String Compaction (Java 9+, still relevant in 11)
String since Java 9 uses a byte[] with a coder flag: Latin-1 strings (ISO 8859-1) use 1 byte per character; others use UTF-16 (2 bytes). Java 11’s new strip() / isBlank() methods are implemented to work correctly with both encodings, which is why they are not just thin wrappers around trim().
HttpClient Internals
HttpClient is built on Java’s NIO event loop. A single thread can manage many concurrent connections via selector-based I/O, and it automatically negotiates HTTP/2 multiplexing when the server supports it — multiple logical request/response streams share one TCP connection.
NestMate Bytecode
The NestHost and NestMembers class-file attributes (new in Java 11) encode the nest relationship directly. The JVM’s invokevirtual and getfield access checks are nest-aware, so no synthetic accessor methods appear in the .class file or in stack traces anymore.
Related Topics
- Java 8 Features — lambdas, streams, and the Optional API that Java 11 builds on
- Java 17 LTS Features — the next major LTS with sealed classes, records, and pattern matching
- var Keyword — local variable type inference introduced in Java 10, extended in Java 11
- NIO.2: Path & Files — the NIO.2 API that
Files.readString()andFiles.writeString()extend - Garbage Collection Deep-Dive — full coverage of ZGC, G1, and other collectors
- HttpURLConnection — the older HTTP API that
HttpClientreplaces