Java 11 new features and enhancements

Java 11 (released on September 2018) includes many important and useful updates. Let’s see the new features and improvements, it brings for developers and architects.

1. HTTP Client API

Java had HttpURLConnection class for long time for HTTP communication. But over the time, requirements have gone complex and more demanding in applications. Before Java 11, developers had to resort to feature-rich libraries like Apache HttpComponents or OkHttp etc.

We saw Java 9 release to include an HttpClient implementation as an experimental feature. It has grown over time and now a final feature of Java 11. Now the Java applications can make HTTP communications without the need to any external dependency.

1.1. How to use HttpClient

A typical HTTP interaction with the java.net.http module looks like-

  • Create an instance of HttpClient and configure it as needed.
  • Create an instance of HttpRequest and populate the information.
  • Pass the request to the client, perform the request, and retrieve an instance of HttpResponse.
  • Process the information contained in the HttpResponse.

HTTP API can handle synchronous and asynchronous communication. Let’s see a quick example.

1.2. Synchronous request example

Notice how the http client API uses builder pattern for creating complex objects.

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

HttpClient httpClient = HttpClient.newBuilder()
				        .connectTimeout(Duration.ofSeconds(10))
				        .build();                                  

try 
{
    String urlEndpoint = "https://postman-echo.com/get";
    URI uri = URI.create(urlEndpoint + "?foo1=bar1&foo2=bar2");
    HttpRequest request = HttpRequest.newBuilder()
						            .uri(uri)
						            .build();                              
    HttpResponse<String> response = httpClient.send(request,
            							HttpResponse.BodyHandlers.ofString()); 
} catch (IOException | InterruptedException e) {
    throw new RuntimeException(e);
}

System.out.println("Status code: " + response.statusCode());                            
System.out.println("Headers: " + response.headers().allValues("content-type"));               
System.out.println("Body: " + response.body()); 

1.2. Asynchronous request example

Asynchronous communication is useful if we don’t want to wait for the response. We provide callback handler, which gets executed when response is available.

Notice the use of sendAsync() method to send asynchronous request.

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toList;

final List<URI> uris = Stream.of(
				        "https://www.google.com/",
				        "https://www.github.com/",
				        "https://www.yahoo.com/"
				        ).map(URI::create).collect(toList());      

HttpClient httpClient = HttpClient.newBuilder()
				        .connectTimeout(Duration.ofSeconds(10))
				        .followRedirects(HttpClient.Redirect.ALWAYS)
				        .build();

CompletableFuture[] futures = uris.stream()
					        .map(uri -> verifyUri(httpClient, uri))
					        .toArray(CompletableFuture[]::new);     

CompletableFuture.allOf(futures).join();           

private CompletableFuture<Void> verifyUri(HttpClient httpClient, 
                                          URI uri) 
{
    HttpRequest request = HttpRequest.newBuilder()
						            .timeout(Duration.ofSeconds(5))
						            .uri(uri)
						            .build();

    return httpClient.sendAsync(request,HttpResponse.BodyHandlers.ofString())
			            .thenApply(HttpResponse::statusCode)
			            .thenApply(statusCode -> statusCode == 200)
			            .exceptionally(ex -> false)
			            .thenAccept(valid -> 
			            {
			                if (valid) {
			                    System.out.println("[SUCCESS] Verified " + uri);
			                } else {
			                    System.out.println("[FAILURE] Could not " + "verify " + uri);
			                }
			            });                                    
}

2. Launch Single-File Programs Without Compilation

Traditionally, for every program that we’d like to execute, we need to first compile it. It seems unnecessarily lengthy process for small programs for testing purposes.

Java 11 changes it and now we can execute Java source code contained in a single file without the need to compile it first.

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

To execute above class, run it directly with java command.

$ java HelloWorld.java

Hello World!

Note that the program cannot use any external dependencies other than the java.base module. And program can be only single-file program.

3. String API Changes

3.1. String.repeat(Integer)

This method simply repeats a string n times. It returns a string whose value is the concatenation of given string repeated N times.

If this string is empty or count is zero then the empty string is returned.

public class HelloWorld 
{
    public static void main(String[] args) 
    {
    	String str = "1".repeat(5);

        System.out.println(str);	//11111
    }
}

3.2. String.isBlank()

This method indicates whether a string is empty or contains only white-spaces. Previously, we have been using it from Apache’s StringUtils.java.

public class HelloWorld 
{
    public static void main(String[] args) 
    {
    	"1".isBlank();	//false

        "".isBlank();	//true

        "    ".isBlank();	//true
    }
}

3.3. String.strip()

This method takes care of removing leading and trailing white-spaces. We can be even more specific by removing just the leading characters by using String.stripLeading() or just the trailing characters by using String.stripTrailing().

public class HelloWorld 
{
    public static void main(String[] args) 
    {
    	"   hi  ".strip();	//"hi"

       "   hi  ".stripLeading();	//"hi   "

       "   hi  ".stripTrailing();	//"   hi"
    }
}

3.4. String.lines()

This method helps in processing multi-line texts as a Stream.

public class HelloWorld 
{
    public static void main(String[] args) 
    {
    	String testString = "hello\nworld\nis\nexecuted";

	    List<String> lines = new ArrayList<>();

	    testString.lines().forEach(line -> lines.add(line));

	    assertEquals(List.of("hello", "world", "is", "executed"), lines);
    }
}

4. Collection.toArray(IntFunction)

Before Java 11, converting a collection to array was not straightforward. Java 11 makes the conversion more convenient.

public class HelloWorld 
{
    public static void main(String[] args) 
    {
    	List<String> names = new ArrayList<>();
	    names.add("alex");
	    names.add("brian");
	    names.add("charles");

	    String[] namesArr1 = names.toArray(new String[names.size()]);		//Before Java 11

	    String[] namesArr2 = names.toArray(String[]::new);					//Since Java 11
    }
}

5. Files.readString() and Files.writeString()

Using these overloaded methods, Java 11 aims to reduce a lot of boilerplate code which makes much easier to read and write files.

public class HelloWorld 
{
    public static void main(String[] args) 
    {
    	//Read file as string
    	URI txtFileUri = getClass().getClassLoader().getResource("helloworld.txt").toURI();

    	String content = Files.readString(Path.of(txtFileUri),Charset.defaultCharset());

    	//Write string to file
    	Path tmpFilePath = Path.of(File.createTempFile("tempFile", ".tmp").toURI());

    	Path returnedFilePath = Files.writeString(tmpFilePath,"Hello World!", 
    								Charset.defaultCharset(), StandardOpenOption.WRITE);
    }
}

6. Optional.isEmpty()

Optional is a container object which may or may not contain a non-null value. If no value is present, the object is considered empty.

Previously existing method isPresent() returns true if a value is present, otherwise false. Sometimes, it forces us to write negative conditions which are not readable.

isEmpty() method is reverse of isPresent() method and returns false if a value is present, otherwise true.

So we do not to write negative conditions in any case. Use any of these two methods when appropriate.

public class HelloWorld 
{
    public static void main(String[] args) 
    {
    	String currentTime = null;

	    assertTrue(!Optional.ofNullable(currentTime).isPresent());	//It's negative condition
	    assertTrue(Optional.ofNullable(currentTime).isEmpty());		//Write it like this

	    currentTime = "12:00 PM";

	    assertFalse(!Optional.ofNullable(currentTime).isPresent());	//It's negative condition
	    assertFalse(Optional.ofNullable(currentTime).isEmpty());	//Write it like this
    }
}

Drop me your questions related to these new API changes in Java 11.

Happy Learning !!

Ref : Java 11 release doc

Was this post helpful?

Join 7000+ Fellow Programmers

Subscribe to get new post notifications, industry updates, best practices, and much more. Directly into your inbox, for free.

Leave a Comment

HowToDoInJava

A blog about Java and its related technologies, the best practices, algorithms, interview questions, scripting languages, and Python.