Java Tutorial

Java Control Statements

Object Oriented Programming

Java Built-in Classes

Java File Handling

Java Error & Exceptions

Java Multithreading

Java Synchronization

Java Networking

Java Collections

Java Interfaces

Java Data Structures

Java Collections Algorithms

Advanced Java

Java Miscellaneous

Java APIs & Frameworks

Java Class References

Java Useful Resources

Java 9 - New Features



JAVA 9 (aka jdk 1.9) is a major release of JAVA programming language development. Its initial version was released on 21 Sep 2017. The main goals of Java 9 release were −

  • To make JDK and Java Standard Edition platform modular based in the sense that it can be scalled down to small computing devices well.

  • To improve the overall security of the JDK and Java Implementations.

  • To make build process and maintainance of java code libraries and large applications easy for for JAVA SE and EE platforms.

  • To design and implement a standard module system for the Java Platform which can be applied on both Platform and JDK easily.

Following is the list of new features supported in Java 9:

Module System

Module System was introduced to enhance Modularity in java code to next level. A module is a self describing collection of data and code. A module can contain packages, configurations specific to particular functionality. A module provides a better access control over its contents. Java library from Java 9 is divided into multiple modules as can be seen using following command.

C:\Users\Mahesh>java --list-modules
java.base@20.0.2
java.compiler@20.0.2
java.datatransfer@20.0.2
java.desktop@20.0.2
...
jdk.xml.dom@20.0.2
jdk.zipfs@20.0.2

Example - Using Module

Below snippet is defining a module declared in module-info.java file in root folder of the application.

module com.tutorialspoint.greetings { 
   requires com.tutorialspoint.util;
   requires static com.tutorialspoint.logging;
   requires transitive com.tutorialspoint.base;
   
   exports com.tutorialspoint.greetings.HelloWorld;
   opens com.tutorialspoint.greetings.HelloWorld;
}

Here we've stated that our module is dependent on three modules and exporting a public Class to be used by outside world and allows reflection to inspect a particular class. By default, private members of modules are not accessible via reflection.

REPL

REPL stands for Read Evaluate Print Loop. A REPL EngineJShell was introduced in Java 9 as an interactive console to run arbitrary snippet of java code in console without need to save and compile java code file. JShell reads each line entered, evaluates it and then print the result and then again becomes ready for next set of input.

Example - Using JShell as REPL

Following snippet shows how to create variables in JShell. semi-colon is optional. We can create objects as well in JShell. If a variable is not initialized then it is given a default value or null if it is an object reference. Once a variable is created, it can be used as shown in the last statement where we've used the string variable to print its value.

Example

In following example, we've created variables, evaluate expressions, created date objects.

jshell> int i = 10
i ==> 10

jshell> String name = "Mahesh";
name ==> "Mahesh"

jshell> Date date = new Date()
date ==> Fri Feb 02 14:52:49 IST 2024

jshell> String.format("%d pages read.", 10);
$9 ==> "10 pages read."

jshell> $9
$9 ==> "10 pages read."
jshell> name
name ==> "Mahesh"

Improved JavaDocs

From Java 9, Java now supports HTML5 output generation and provides a search box to generated API documentation.

Example

In this example, we're creating a HTML5 compliant javadoc.

Consider the following code in C:/JAVA folder.

Tester.java

/**
  * @author MahKumar
  * @version 0.1
  */
public class Tester {
   /**
      * Default method to be run to print 
      * <p>Hello world</p>
      * @param args command line arguments
      */
   public static void main(String []args) {
      System.out.println("Hello World");
   }
}

Run the javadoc tool of jdk 9 with -html5 flag to generate new type of documentation.

C:\JAVA> javadoc -d C:/JAVA -html5 Tester.java
Loading source file Tester.java...
Constructing Javadoc information...
Standard Doclet version 9.0.1
Building tree for all the packages and classes...
Generating C:\JAVA\Tester.html...
Generating C:\JAVA\package-frame.html...
Generating C:\JAVA\package-summary.html...
Generating C:\JAVA\package-tree.html...
Generating C:\JAVA\constant-values.html...
Building index for all the packages and classes...
Generating C:\JAVA\overview-tree.html...
Generating C:\JAVA\index-all.html...
Generating C:\JAVA\deprecated-list.html...
Building index for all classes...
Generating C:\JAVA\allclasses-frame.html...
Generating C:\JAVA\allclasses-frame.html...
Generating C:\JAVA\allclasses-noframe.html...
Generating C:\JAVA\allclasses-noframe.html...
Generating C:\JAVA\index.html...
Generating C:\JAVA\help-doc.html...

It will create the updated java documentation page in D:/test directory and you will see the following output.

javadoc output in java 9

Multirelease JAR

Multirelease JAR feature in Java 9 enhances the JAR format so that multiple, Java release-specific versions of class files can coexist in a single archive.

In multi-release Jar format, a jar file can have different versions of Java classes or resources that can be maintained and used as per the platform. In JAR, a file MANIFEST.MF file has an entry Multi-Release: true in its main section. META-INF directory also contains a versions subdirectory whose subdirectories (starting with 9 for Java 9 ) store version-specific classes and resource files.

Using MANIFEST.MF, we can specific Java 9 or higher version-specific classes in separate locations as shown below −

Java Multi-Release Jar Files Directory Structure Example

jar root
   - Calculator.class
   - Util.class
   - Math.class
   - Service.class
   META-INF
      - versions
      - 9
         - Util.class
         - Math.class
      - 10
         - Util.class
         - Math.class

Now if JRE is not support Multi-release jar, then it will choose the root level classes to load and execute otherwise, version specific classes will be loaded. For example, if above jar is used in Java 8, then root level Util.class will be used. If same jar is executed by Java 9, then java 9 version specific class will be picked and so on. This way, third party libraries/frameworks can support new features without changing their source codes which was written targeting the lower versions.

Collection Factory Methods Improvements

In Java 9, New static factory methods are added to List, Set, and Map interfaces to create immutable instances of those collections. These factory methods are mainly convenience factory methods in order to create a collection in less verbose and in concise way.

Example of List Interface Factory Methods Before Java 9

Here, we are creating unmodifiable list before Java 9.

package com.tutorialspoint;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Tester {
   public static void main(String[] args) {
      List<String> list = new ArrayList<>();

      list.add("Java");
      list.add("HTML 5");
      list.add("C");
      list = Collections.unmodifiableList(list);
      System.out.println(list);
   }  
}

Let us compile and run the above program, this will produce the following result −

[Java, HTML 5, C]

Example of List Interface Factory Methods in Java 9

Here, we are creating unmodifiable list in Java 9.

package com.tutorialspoint;

import java.util.List;

public class Tester {
   public static void main(String[] args){
	   List<String> list =  List.of("Java","HTML 5","C");
	   System.out.println(list);
   }  
}

Let us compile and run the above program, this will produce the following result −

[Java, HTML 5, C]

Private Interface Methods

Private and static private interface methods were introduced in Java 9. Being a private method, such a method cannot be accessed via implementing class or sub-interface. This methods were introduced to allow encapsulation where the implementation of certain method will be kept in interface only. It helps to reduce the duplicity, increase maintainablity and to write clean code.

Example - Private method in Interface from Java 9

package com.tutorialspoint;

interface util {
   public default int operate(int a, int b) {
      return sum(a, b);
   }
   private int sum(int a, int b) {
      return a + b;
   } 
}

public class Tester implements util {
   public static void main(String[] args) {
      Tester tester = new Tester();
      System.out.println(tester.operate(2, 3));
   }
}

Output

Let us compile and run the above program, this will produce the following result −

5

Similary, we can have private static method which can be called from static and non-static methods.

Process API Improvements

In Java 9 Process API which is responsible to control and manage operating system processes has been improved considerably. ProcessHandle Class now provides process's native process ID, start time, accumulated CPU time, arguments, command, user, parent process, and descendants. ProcessHandle class also provides method to check processes' liveness and to destroy processes. It has onExit method, the CompletableFuture class can perform action asynchronously when process exits.

Spawning a new Process Example

In this example, we've created a new Process for notepad and started it using ProcessBuilder. Using ProcessHandle.Info interface, we're getting the process information of the newly spawned process.

package com.tutorialspoint;

import java.time.ZoneId;
import java.util.stream.Stream;
import java.util.stream.Collectors;
import java.io.IOException;

public class Tester {
   public static void main(String[] args) throws IOException {
      ProcessBuilder pb = new ProcessBuilder("notepad.exe");
      String np = "Not Present";
      Process p = pb.start();
      ProcessHandle.Info info = p.info();
      System.out.printf("Process ID : %s%n", p.pid());
      System.out.printf("Command name : %s%n", info.command().orElse(np));
      System.out.printf("Command line : %s%n", info.commandLine().orElse(np));

      System.out.printf("Start time: %s%n",
         info.startInstant().map(i -> i.atZone(ZoneId.systemDefault())
         .toLocalDateTime().toString()).orElse(np));

      System.out.printf("Arguments : %s%n",
         info.arguments().map(a -> Stream.of(a).collect(
         Collectors.joining(" "))).orElse(np));

      System.out.printf("User : %s%n", info.user().orElse(np));
   } 
}

Output

You will see the similar output.

Process ID : 5580
Command name : C:\Program Files\WindowsApps\Microsoft.WindowsNotepad_11.2401.26.0_x64__8wekyb3d8bbwe\Notepad\Notepad.exe
Command line : Not Present
Start time: 2024-04-02T17:07:14.305
Arguments : Not Present
User : DESKTOP\Tutorialspoint

Stream API Improvements

Streams were introduced in Java 8 to help developers perform aggregate operations from a sequence of objects. With Java 9, few more methods are added to make streams better.

takeWhile(Predicate Interface) Method

Syntax

default Stream<T> takeWhile(Predicate<? super T> predicate)

takeWhile method takes all the values until the predicate returns false. It returns, in case of ordered stream, a stream consisting of the longest prefix of elements taken from this stream matching the given predicate.

dropWhile(Predicate Interface)

Syntax

default Stream<T> dropWhile(Predicate<? super T> predicate)

dropWhile method throw away all the values at the start until the predicate returns true. It returns, in case of ordered stream, a stream consisting of the remaining elements of this stream after dropping the longest prefix of elements matching the given predicate.

iterate Method

Syntax

static <T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)

iterate method now has hasNext predicate as parameter which stops the loop once hasNext predicate returns false.

ofNullable

Syntax

static <T> Stream<T> ofNullable(T t)

ofNullable method is introduced to prevent NullPointerExceptions and to avoid null checks for streams. This method returns a sequential Stream containing single element, if non-null, otherwise returns an empty Stream.

Try with Resources Improvements

Prior to Java 9, resources are to be declared before try or inside try statement as shown below in given example. In this example, we'll use BufferedReader as resource to read a string and then BufferedReader is to be closed.

Java 9 onwards

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;

public class Tester {
   public static void main(String[] args) throws IOException {
      System.out.println(readData("test"));
   } 
   static String readData(String message) throws IOException {
      Reader inputString = new StringReader(message);
      BufferedReader br = new BufferedReader(inputString);
      try (br) {
         return br.readLine();
      }
   }
}

Output

Let us compile and run the above program, this will produce the following result −

test

Enhanced @Deprecated Annotation

@Deprecated annotation was introduced in java 5 version. A program element annotated with @Deprecated means it should not be used for any of the following reasons −

  • Its usage may leads to errors.
  • It may be incompatible in future version.
  • It may be removed in future version.
  • A better and efficient alternative has superseeded it.

Compiler generates warnings whenever a deprecated element is used. With Java 9, two new enhancements are made to @Deprecated annotation.

  • forRemoval − Indicates whether the annotated element is subject to removal in a future version. The default value is false.

  • since − Returns the version in which the annotated element became deprecated. The default value is the empty string.

Deprecated with since

Following example of Boolean class javadoc on Java 9 illustrate the use of since attribute on @Deprecated annotation.

Boolean Class

Boolean Class javadoc

Deprecated with forRemoval

Following example of System class javadoc on Java 9 illustrate the use of forRemoval attribute on @Deprecated annotation.

System Class

System Class javadoc

Inner Class Diamond Operator

In Java 9, the diamond operator can be used with an anonymous class as well to simplify code and improve readability.

Example

In below example, we've created anonymous classes for an abstract class Handler accepting a generic argument but without the object type while creating the anonymous class as we need not to pass the type argument. Compiler infers the type itself.

public class Tester {
   public static void main(String[] args) {
      // create an Anonymous class to handle 1
	  // Here we do not need to pass Type arguments in diamond operator 
	  // as Java 9 compiler can infer the type automatically
      Handler<Integer> intHandler = new Handler<>(1) {
         @Override
         public void handle() {
            System.out.println(content);
         }
      };
      intHandler.handle();
      Handler<? extends Number> intHandler1 = new Handler<>(2) {
         @Override
         public void handle() {
            System.out.println(content);
         }
      };
      intHandler1.handle();
      Handler<?> handler = new Handler<>("test") {
         @Override
         public void handle() {
            System.out.println(content);
         }
      };

      handler.handle();    
   }  
}

abstract class Handler<T> {
   public T content;

   public Handler(T content) {
      this.content = content; 
   }
   
   abstract void handle();
}

Output

Let us compile and run the above program, this will produce the following result −

1
2
Test

Multiresolution Image API

Multi-resolution image API was introduced in Java 9. This API supports multiple images with different resolution variants. This API allows a set of images with different resolution to be used as a single multi-resolution image.

Consider the following images.

mini logo.png logo.png large logo.png

These are three images of a logo with different sizes.

Now in order to work with these three images, Java 9 onwards, Multi-resolution Image API can be used as single API to get all variants or a particular variant to be displayed.

// read all images into one multiresolution image
MultiResolutionImage multiResolutionImage = 
   new BaseMultiResolutionImage(images.toArray(new Image[0]));

Here MultiResolutionImage and BaseMultiResolutionImage classes are part of java.awt.image package.

Following are major operations of multi-resolution image.

  • Image getResolutionVariant(double destImageWidth, double destImageHeight) − Gets a specific image which is best variant to represent this logical image at the indicated size.

  • List<Image> getResolutionVariants() − Gets a readable list of all resolution variants.

Example - Get All variants

In this example, we've loaded three images and store them in MultiResolutionImage. Then using getResolutionVariants() method, we're checking all the available image variants in this multi-resolution image and printing it.

package com.tutorialspoint;

import java.awt.Image;
import java.awt.image.BaseMultiResolutionImage;
import java.awt.image.MultiResolutionImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;

public class Tester {
   public static void main(String[] args) throws IOException, MalformedURLException {

	  // prepare a list of urls of all images
      List<String> imgUrls = List.of("http://www.tutorialspoint.com/java9/images/logo.png",
         "http://www.tutorialspoint.com/java9/images/mini_logo.png",
         "http://www.tutorialspoint.com/java9/images/large_logo.png");

      // create a list of Image object
      List<Image> images = new ArrayList<Image>();

      // Create image objects using image urls
      for (String url : imgUrls) {
         images.add(ImageIO.read(new URL(url)));
      }

      // read all images into one multiresolution image
      MultiResolutionImage multiResolutionImage = 
         new BaseMultiResolutionImage(images.toArray(new Image[0]));

      // get all variants of images
      List<Image> variants = multiResolutionImage.getResolutionVariants();

     
      System.out.println("Total number of images: " + variants.size());
     
      // print all the images
      for (Image img : variants) {
         System.out.println(img);
      }     
   }  
}

Output

Let us compile and run the above program, this will produce the following result −

Total number of images: 3
BufferedImage@7ce6a65d: type = 6 ColorModel: #pixelBits = 32 numComponents = 4 
color space =java.awt.color.ICC_ColorSpace@548ad73b transparency = 3 
has alpha = true isAlphaPre = false ByteInterleavedRaster: width =311 
height = 89 #numDataElements 4 dataOff[0] = 3

BufferedImage@4c762604: type = 6 ColorModel: #pixelBits = 32 numComponents = 4 
color space =java.awt.color.ICC_ColorSpace@548ad73b transparency = 3 
has alpha = true isAlphaPre = false ByteInterleavedRaster: width =156 
height = 45 #numDataElements 4 dataOff[0] = 3

BufferedImage@2641e737: type = 6 ColorModel: #pixelBits = 32 numComponents = 4 
color space =java.awt.color.ICC_ColorSpace@548ad73b transparency = 3 
has alpha = true isAlphaPre = false ByteInterleavedRaster: width =622 
height = 178 #numDataElements 4 dataOff[0] = 3

CompletableFuture API Enhancement

CompletableFuture class was introduced in Java 8 to represent the Future which can be completed by setting its value and status explicity. It can be used as java.util.concurrent.CompletionStage. It supports dependent functions and actions which got triggered upon the future's completion. In java 9 CompletableFuture API has been enhanced further. Following are the relevant changes done to the API.

  • Support for delays and timeouts.
  • Improved support for subclassing.
  • New factory methods added.

Support for delays and timeouts

public CompletableFuture<T> completeOnTimeout(T value, long timeout, TimeUnit unit)

This method completes this CompletableFuture with the given value if not otherwise completed before the given timeout.

public CompletableFuture<T> orTimeout(long timeout, TimeUnit unit)

This method exceptionally completes this CompletableFuture with a TimeoutException if not otherwise completed before the given timeout.

Improved support for subclassing

public Executor defaultExecutor()

It returns the default Executor used for async methods that do not specify an Executor. This method may be overridden in subclasses to return an Executor to provide one independent thread as minimum.

public <U> CompletableFuture<U> newIncompleteFuture()

Returns a new incomplete CompletableFuture of the type to be returned by a CompletionStage method. Subclasses of CompletableFuture class should override this method to return an instance of the same class as this CompletableFuture. The default implementation returns an instance of class CompletableFuture.

New factory Methods

public static <U> CompletableFuture<U> completedFuture(U value)

This factory method returns a new CompletableFuture which is already completed with the given value.

public static <U> CompletionStage<U> completedStage(U value)

This factory method returns a new CompletionStage which is already completed with the given value and supports only those methods present in interface CompletionStage.

public static <U> CompletionStage<U> failedStage(Throwable ex)

This factory method returns a new CompletionStage which is already completed exceptionally with the given exception and supports only those methods present in interface CompletionStage.

Miscellaneous features

Apart from mentioned features, with Java 9, a lot more enhancements are done to JDK platform. Some of them are listed below.

  • GC (Garbage Collector) Improvements
  • Stack-Walking API
  • Filter Incoming Serialization Data
  • Deprecate the Applet API
  • Indify String Concatenation
  • Enhanced Method Handles
  • Java Platform Logging API and Service
  • Compact Strings
  • Parser API for Nashorn
Advertisements