BLOG POSTS
    MangoHost Blog / Java 11 Features – What’s New and How to Use Them
Java 11 Features – What’s New and How to Use Them

Java 11 Features – What’s New and How to Use Them

Java 11 represents a significant milestone in the Java ecosystem, being the first Long Term Support (LTS) release after Java 8. Released in September 2018, it brought substantial performance improvements, new language features, and important API enhancements that have made it the go-to choice for modern enterprise applications. This post will walk you through the most impactful Java 11 features, show you how to implement them in real-world scenarios, and help you understand why upgrading from Java 8 might be one of the best decisions for your applications.

String API Enhancements

Java 11 introduced several new methods to the String class that significantly reduce boilerplate code. These additions make string manipulation more intuitive and performance-oriented.

// isBlank() - checks if string is empty or contains only whitespace
String emptyString = "   ";
System.out.println(emptyString.isBlank()); // true
System.out.println("Hello".isBlank()); // false

// lines() - returns stream of lines
String multiline = "Line 1\nLine 2\nLine 3";
multiline.lines()
    .forEach(System.out::println);

// strip(), stripLeading(), stripTrailing() - Unicode-aware trimming
String unicode = " \u2000 Hello World \u2000 ";
System.out.println(unicode.strip()); // "Hello World"
System.out.println(unicode.stripLeading()); // "Hello World \u2000 "

// repeat() - repeats string n times
String repeated = "Java".repeat(3);
System.out.println(repeated); // "JavaJavaJava"

The performance difference between the new strip() methods and the legacy trim() is notable. While trim() only removes ASCII whitespace characters, strip() handles all Unicode whitespace characters and uses more efficient algorithms.

Method Unicode Support Performance Use Case
trim() ASCII only Good Legacy applications
strip() Full Unicode Better Modern internationalized apps

Local Variable Syntax for Lambda Parameters

Java 11 extends the var keyword introduced in Java 10 to lambda expressions, enabling you to add annotations to lambda parameters while maintaining type inference.

// Before Java 11
Consumer consumer1 = (String s) -> System.out.println(s);

// Java 11 - using var with annotations
Consumer consumer2 = (@NonNull var s) -> {
    System.out.println(s.toUpperCase());
};

// Real-world example with validation
List names = Arrays.asList("John", "Jane", "Bob");
names.stream()
    .filter((@NonNull var name) -> name.length() > 3)
    .forEach(System.out::println);

This feature is particularly useful when you need to add annotations like @NonNull, @Nullable, or custom validation annotations to lambda parameters without losing the benefits of type inference.

HTTP Client API

Java 11 standardized the HTTP Client API that was incubated in Java 9 and 10. This modern, fully-featured HTTP client supports HTTP/2, WebSocket, and provides both synchronous and asynchronous request handling.

import java.net.http.*;
import java.net.URI;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;

// Creating an HTTP client
HttpClient client = HttpClient.newBuilder()
    .version(HttpClient.Version.HTTP_2)
    .connectTimeout(Duration.ofSeconds(20))
    .build();

// Synchronous GET request
HttpRequest getRequest = HttpRequest.newBuilder()
    .uri(URI.create("https://api.github.com/users/octocat"))
    .build();

HttpResponse getResponse = client.send(getRequest, 
    HttpResponse.BodyHandlers.ofString());

System.out.println("Status: " + getResponse.statusCode());
System.out.println("Body: " + getResponse.body());

// Asynchronous POST request
String json = "{\"name\":\"test\",\"job\":\"developer\"}";
HttpRequest postRequest = HttpRequest.newBuilder()
    .uri(URI.create("https://reqres.in/api/users"))
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(json))
    .build();

CompletableFuture> asyncResponse = 
    client.sendAsync(postRequest, HttpResponse.BodyHandlers.ofString());

asyncResponse.thenApply(HttpResponse::body)
    .thenAccept(System.out::println);
Feature Java 11 HTTP Client Apache HttpClient OkHttp
HTTP/2 Support Yes Yes Yes
WebSocket Yes No Yes
Reactive Streams Yes No No
Built-in Yes No No

File API Improvements

Java 11 enhanced the Files class with convenient methods for reading and writing files as strings, making file I/O operations much more concise.

import java.nio.file.*;
import java.io.IOException;

// Reading file as string
Path filePath = Paths.get("example.txt");
try {
    String content = Files.readString(filePath);
    System.out.println(content);
} catch (IOException e) {
    e.printStackTrace();
}

// Writing string to file
String data = "Hello World\nFrom Java 11";
try {
    Files.writeString(filePath, data);
    
    // Append to file
    Files.writeString(filePath, "\nAppended line", 
        StandardOpenOption.APPEND);
} catch (IOException e) {
    e.printStackTrace();
}

// Working with different charsets
try {
    String utf8Content = Files.readString(filePath, StandardCharsets.UTF_8);
    Files.writeString(Paths.get("output.txt"), utf8Content, 
        StandardCharsets.UTF_8);
} catch (IOException e) {
    e.printStackTrace();
}

Collection to Array Method

The new toArray() method in Collection interface allows type-safe conversion to arrays with a more intuitive API.

import java.util.*;

List list = Arrays.asList("apple", "banana", "orange");

// Java 11 way - cleaner and type-safe
String[] array1 = list.toArray(String[]::new);

// Compare with Java 8 approach
String[] array2 = list.toArray(new String[0]);

// Real-world example with filtering
List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer[] evenNumbers = numbers.stream()
    .filter(n -> n % 2 == 0)
    .collect(Collectors.toList())
    .toArray(Integer[]::new);

System.out.println(Arrays.toString(evenNumbers)); // [2, 4, 6, 8, 10]

Nest-Based Access Control

Java 11 introduced nest-based access control, improving the way nested classes access each other’s private members. This feature primarily affects the JVM internals and reduces the number of synthetic bridge methods.

public class OuterClass {
    private String outerField = "outer";
    
    class InnerClass {
        private String innerField = "inner";
        
        void accessOuter() {
            // Direct access without synthetic methods in Java 11+
            System.out.println(outerField);
        }
    }
    
    void accessInner() {
        InnerClass inner = new InnerClass();
        // Direct access to private field
        System.out.println(inner.innerField);
    }
    
    // Check if classes are nestmates
    public static void main(String[] args) {
        System.out.println(OuterClass.class.isNestmateOf(InnerClass.class)); // true
        System.out.println(Arrays.toString(OuterClass.class.getNestMembers()));
    }
}

Running Single-File Source Programs

Java 11 introduced the ability to run single Java source files directly without explicit compilation, making it easier for scripting and quick testing.

# Create a simple Java file
cat > Hello.java << EOF
public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello from Java 11!");
        if (args.length > 0) {
            System.out.println("Arguments: " + String.join(", ", args));
        }
    }
}
EOF

# Run directly without javac
java Hello.java arg1 arg2 arg3

# You can also use shebang for Unix-like systems
cat > script.java << EOF
#!/usr/bin/java --source 11
public class Script {
    public static void main(String[] args) {
        System.out.println("Running as script!");
    }
}
EOF

chmod +x script.java
./script.java

JVM and Performance Improvements

Java 11 includes several JVM improvements that enhance performance and reduce memory footprint:

  • Epsilon Garbage Collector: A no-op garbage collector for performance testing
  • ZGC (Experimental): Low-latency garbage collector for large heaps
  • Dynamic Class-File Constants: Reduces memory usage and improves performance
  • Improved G1 Garbage Collector: Better performance for mixed workloads
# Enable Epsilon GC for testing
java -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC MyApp

# Enable ZGC (experimental in Java 11)
java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC MyApp

# G1GC tuning options
java -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1HeapRegionSize=16m MyApp

Migration Considerations and Common Issues

When migrating from Java 8 to Java 11, you'll encounter several changes that require attention:

  • Removed modules: Java EE and CORBA modules are no longer included
  • JavaFX separation: JavaFX is now a separate download
  • Deprecated features: Several APIs have been deprecated or removed
# Common migration issues and solutions

# 1. Missing Java EE modules - add dependencies
# For JAXB

    javax.xml.bind
    jaxb-api
    2.3.1


# 2. JavaFX applications
# Add JavaFX as external dependency

    org.openjfx
    javafx-controls
    11.0.2


# 3. Check for usage of removed APIs
# Use jdeps tool to analyze dependencies
jdeps --jdk-internals myapp.jar

Best Practices and Real-World Applications

Here are practical recommendations for adopting Java 11 features in production environments:

  • HTTP Client: Replace third-party HTTP libraries for new projects to reduce dependencies
  • String methods: Use strip() instead of trim() for better Unicode handling
  • File I/O: Leverage new Files methods for configuration file handling
  • Single-file execution: Great for DevOps scripts and automation tools
// Example: Configuration file processor using Java 11 features
import java.nio.file.*;
import java.util.stream.Collectors;

public class ConfigProcessor {
    public static void processConfig(String filename) throws Exception {
        Path configPath = Paths.get(filename);
        
        // Read and process configuration
        String content = Files.readString(configPath);
        
        String processed = content.lines()
            .filter(line -> !line.strip().isEmpty())
            .filter(line -> !line.strip().startsWith("#"))
            .map(String::strip)
            .collect(Collectors.joining("\n"));
        
        // Write processed config
        Files.writeString(Paths.get("processed_" + filename), processed);
    }
}

Performance benchmarks show that Java 11 applications typically see 10-15% better performance compared to Java 8, with significantly lower memory usage due to improved garbage collection and runtime optimizations. The combination of new language features and JVM improvements makes Java 11 an excellent choice for both new development and migration projects.

For comprehensive documentation on all Java 11 features, visit the official Oracle Java 11 documentation and the OpenJDK Java 11 project page.



This article incorporates information and material from various online sources. We acknowledge and appreciate the work of all original authors, publishers, and websites. While every effort has been made to appropriately credit the source material, any unintentional oversight or omission does not constitute a copyright infringement. All trademarks, logos, and images mentioned are the property of their respective owners. If you believe that any content used in this article infringes upon your copyright, please contact us immediately for review and prompt action.

This article is intended for informational and educational purposes only and does not infringe on the rights of the copyright owners. If any copyrighted material has been used without proper credit or in violation of copyright laws, it is unintentional and we will rectify it promptly upon notification. Please note that the republishing, redistribution, or reproduction of part or all of the contents in any form is prohibited without express written permission from the author and website owner. For permissions or further inquiries, please contact us.

Leave a reply

Your email address will not be published. Required fields are marked