Java 11 Features
Java 11 (September 2018) is an LTS (Long-Term Support) release — meaning companies trust it in production, since Oracle supports it for a long time (the most popular LTS version after Java 8).
It brought several small but useful additions: new String methods (isBlank, strip, lines, repeat), easier file I/O with Files.readString()/writeString(), a new standard HTTP Client API, and the ability to run a single-file Java program directly without compiling it first.
In interviews, Java 11 features often come up in follow-ups like "what's new after Java 8" — especially var in lambdas, the new String methods, and the HTTP Client API.
// Naye String methods
System.out.println(" ".isBlank()); // true
System.out.println("Hi".repeat(3)); // HiHiHi
// Files.readString() — ek line mein file padho
String content = Files.readString(Paths.get("notes.txt"));
// var in lambda parameters
BiFunction<Integer, Integer, Integer> add = (var a, var b) -> a + b;Java 10 introduced "var" for local variables (var x = 10;), but you couldn't use it in lambda parameters. Java 11 filled that gap — now you can write var in a lambda's parameters too, like (var a, var b) -> a + b.
The benefit shows up when you need to put an annotation on a parameter (like @NotNull var x) — without var, putting an annotation on a lambda parameter wasn't syntactically possible. Writing just the name, (a, b) -> ..., and writing var, (var a, var b) -> ..., compile to exactly the same thing — no performance difference, this feature exists purely for annotation support.
Rule: either write var on all the parameters, or on none of them — you can't mix, so (var a, b) -> ... is invalid (a compile error).
// Normal lambda
BiFunction<Integer, Integer, Integer> add1 = (a, b) -> a + b;
// var ke saath (Java 11+)
BiFunction<Integer, Integer, Integer> add2 = (var a, var b) -> a + b;
// Annotation ke saath — sirf var se hi possible
// (var a, @Deprecated var b) -> a + b;isBlank() checks whether a String is empty or contains only whitespace — isEmpty() only checks if the length is 0, but for " " (just spaces), isEmpty() returns false while isBlank() returns true. This was a common source of bugs before Java 11.
strip()/stripLeading()/stripTrailing() work like trim(), but are Unicode-aware — trim() only understands ASCII whitespace (characters with a code less than or equal to space), while strip() also handles Unicode whitespace (like non-breaking spaces). Modern code prefers strip() over trim().
lines() splits a String into a Stream<String>, line by line (useful for multi-line text, without manually calling split("\n")). repeat(n) builds a new String by repeating a String n times.
String s = " ";
System.out.println(s.isEmpty()); // false (length > 0 hai)
System.out.println(s.isBlank()); // true (sirf whitespace hai)
String text = "Line1\nLine2\nLine3";
text.lines().forEach(System.out::println); // 3 lines print
System.out.println("Ab".repeat(3)); // "AbAbAb"Before Java 11, reading a whole file into a String meant calling Files.readAllBytes() and manually doing new String(bytes, charset), or collecting a stream from Files.lines() — a bit of boilerplate. Files.readString(path) does all of this in one line.
Files.writeString(path, content) similarly writes a String straight to a file, without manually converting to bytes. Both methods default to UTF-8 (an overload also lets you pass a custom Charset).
// Purana tareeka (Java 11 se pehle):
byte[] bytes = Files.readAllBytes(Paths.get("notes.txt"));
String content = new String(bytes, StandardCharsets.UTF_8);
// Java 11+:
String content2 = Files.readString(Paths.get("notes.txt"));
Files.writeString(Paths.get("output.txt"), "Hello Java 11!");It arrived as an incubator (experimental) module in Java 9, and became the standard java.net.http package in Java 11 — a modern replacement for the old HttpURLConnection (which dates back to Java 1.1, and is clunky and limited).
The new client supports HTTP/2 (by default), gives you both synchronous (send()) and asynchronous (sendAsync(), which returns a CompletableFuture) ways to make requests, and uses a fluent Builder pattern to build requests — reducing the need for third-party libraries (like Apache HttpClient or OkHttp) for simple use-cases.
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/data"))
.GET()
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());Normally in Java you'd first compile with javac to create a .class file, then run it with java. Since Java 11, you can run a single .java file directly with the java command, without an explicit compile step — java Hello.java. The JVM compiles it in the background and runs it directly in memory (no .class file gets written to disk).
This feature is only for single-file programs (not multi-file projects) — it's very useful for quick scripts, prototyping, or learning, since one command replaces the usual two (javac + java).
// Hello.java
public class Hello {
public static void main(String[] args) {
System.out.println("Direct run, no compile step!");
}
}
// Terminal mein:
// $ java Hello.java
// Direct run, no compile step!Collection.toArray(IntFunction<T[]> generator) is a new overload — previously you'd write something like toArray(new String[0]), now you can use a cleaner method reference like toArray(String[]::new), where the collection itself figures out the array size.
Optional.isEmpty() was added — previously there was only isPresent(), so checking "if there's no value" meant writing !optional.isPresent(). isEmpty() is more readable (avoids the negation).
List<String> names = List.of("Riya", "Aman");
String[] arr = names.toArray(String[]::new); // naya, cleaner tareeka
Optional<String> opt = Optional.empty();
if (opt.isEmpty()) {
System.out.println("Koi value nahi hai");
}Sometimes you need to negate an existing Predicate (like String::isBlank) — you can't negate a method reference directly (String::isBlank has no negate() to call on it directly), so previously you had to write a lambda instead: s -> !s.isBlank().
Predicate.not(predicate) is a static helper that cleans this up — Predicate.not(String::isBlank) directly negates the method reference, without writing an extra lambda.
List<String> words = List.of("Java", "", " ", "Code");
// Java 11 se pehle:
words.stream().filter(s -> !s.isBlank()).forEach(System.out::println);
// Java 11+, Predicate.not() ke saath:
words.stream().filter(Predicate.not(String::isBlank)).forEach(System.out::println);Epsilon GC (JEP 318) is a "no-op" (does-nothing) garbage collector — it allocates memory, but never collects (cleans up) any of it. Sounds strange, but it's useful for performance testing (measuring GC's overhead) or very short-lived programs (that finish before they'd ever run out of memory anyway).
ZGC (JEP 333, experimental in this version) is a scalable, low-latency garbage collector that can handle heaps from a few GB up to multiple TB, keeping pause times under 10ms — designed for very large applications where even a small pause is noticeable. For interviews, knowing their names and a one-line purpose is usually enough — deep internals are rarely asked.
| GC | Purpose |
|---|---|
| Epsilon | No-op — only allocates, never collects (for testing/measurement) |
| ZGC | Large heaps, very low pause time (experimental in Java 11) |
| G1 (default since Java 9+) | General-purpose, balanced throughput + pause time |