Java 9 Tutorial

java-9-tutorials-feature-image

With Platform Module System, New Tools, New Core Libraries, Client Technologies and Languages Updates …, we all will be interested in how it makes many cool things for development.

*Note: To configure your IDE for working with Java 9, please visit:
How to configure Java 9 Support for Oxygen (4.7)

I. Java Platform Module System

Java 9 Module System, which is developed under Project Jigsaw, comes to us with the specific goal: to provide reliable configuration and strong flexible encapsulation. That helps application developers, library developers, or Java SE Platform implementors more easilier create a scalable platform, make greater platform integrity, and improve performance.

What is Module?
A module is a named, self-describing collection of:
– code: packages containing types (Java classes, interfaces…)
– data: resources and other kinds of static information.

In summary:
– Class contains fields, methods.
– Package contains Classes, Enums, Interfaces, configuration files…
– Module contains Package and Other data resources.

Module system mechanism provides Readability and Accessibility that control how a module can read others and to be accessed by others.

There are three kinds of module: named module, unnamed module, automatic module.

Java has a java.util.ServiceLoader class that helps to locate service providers at runtime by searching in class path. Now we can also specify service providers and users defined in modules.

>> More details at: Java 9 Module System

II. Tools

1. Jshell – The Java Shell

Java 9 provides an interactive REPL (Read-Eval-Print Loop) tool to test code snippets rapidly without a test project or main method. So we can learn or evaluate Java features easily.

Now we don’t need to create Java Project or define a public static void main(String[] args) method for testing code. Just write and run immediately.

>> More details at: Java 9 JShell – REPL

2. Unified JVM Logging

Java 9 provides a common logging system for JVM components with extremely detailed level, an infrastructure to do the logging. With new command-line option -Xlog for all logging followed settings, Unified JVM Logging gives us a precise, easy-to-configure tool to do a root cause analysis of complex system-level JVM components.

– Log messages are categorized using tags (os, gc, modules…). One message can have multiple tags (tag-set).
– Logging levels: error, warning, info, debug, trace and develop.
– Output supports 3 types: stdout, stderr, or a text file.
– Messages can be “decorated” with: time, uptime, pid, tid, level, tags

>> More details at: Java 9 Unified JVM Logging

3. HTML5 Javadoc

Javadoc is the tool that can generate documentation for API in HTML format. In previous version of JDK, it’s HTML 4.01 – an old standard. JDK 9 Javadoc now supports to generate HTML5 markup, improves search capability and Doclint.

3.1– In JDK 9 which supports HTML5, we just need to add -html5 parameter:

javadoc -sourcepath "E:\Eclipse Java 9\Java9HTML5Javadoc\src" com.javasampleapproach.html5doc -d E:\home\html  -html5

html5-javadoc-generate

3.2– A search box is available on the site that can be used to search for program elements, tagged words and phrases within the documentation. The search functionality is implemented locally and not rely on any server-side computational resources.
html5-javadoc-search

3.3-Xdoclint enables recommended checks for issues in Javadoc comments: bad references, lack of accessibility, missing comments, syntax error and missing HTML tags. By default, -Xdoclint is enabled. We can disable it by -Xdoclint:none.

This is an example for syntax check:
html5-javadoc-xdoclint

>> More details at: Java 9 HTML5 Javadoc

III. Language Updates

1. try-with-resources Improvement

Java 7 introduces a new approach for closing resources by try-with-resources statement. After that, Java 9 try-with-resources makes an improved way of writing code. Now we can simplify our code and keep it cleaner and clearer.

// BufferedReader is declared outside try() block
BufferedReader br = new BufferedReader(new FileReader("C://readfile/input.txt"));

// Java 9 make it simple
try (br) {
	String line;
	while (null != (line = br.readLine())) {
		// processing each line of file
		System.out.println(line);
	}
} catch (IOException e) {
	e.printStackTrace();
}

>> More details at: Java 9 try-with-resources Improvement

2. Private Interface Method

Java 8 provides 2 new features for Interface: default methods and static methods.

But, it still makes us uncomfortable because:
-> We don’t want to public that method, it is just an inner private method which handles a specific function.
-> We don’t want another interface or class which implements this interface can access or inherit that method.

Java 9 Private Interface Method solves the problems by providing a new feature for interface: private method/private static method. Now we can avoid duplicate code and keep encapsulation for interface.

public interface IMyInterface {

	private void method1(String arg) {
		// do something
	}

	private static void method2(Integer arg) {
		// do something
	}
}

>> More details at: Java 9 Private Interface Method

3. Diamond Operator

Java 7 has a new feature called Diamond Operator which helps to make code more readable, but it is still limited with Anonymous Inner Classes.

We will get the compile error: ‘<>‘ cannot be used with anonymous classes if writing code like this:

MyHandler intHandler = new MyHandler<>(10) { // Anonymous Class };
MyHandler handler = new MyHandler<>(""One hundred") { // Anonymous Class };

Java 9 allows the Diamond Operator for Anonymous Inner Classes:

MyHandler intHandler = new MyHandler<>(1) {

	@Override
	public void handle() {
		// handling code...
	}
};

MyHandler intHandler1 = new MyHandler<>(10) {

	@Override
	void handle() {
		// handling code...
	}
};

MyHandler handler = new MyHandler<>("One hundred") {

	@Override
	void handle() {
		// handling code...
	}
};
}

>> More details at: Java 9 Diamond Operator for Anonymous Inner Classes

IV. New Core Libraries

1. Process API

There are new ways of retrieving process information: all processes, current process, children processes and destroying process with Java 9 Process API.

// current Process
ProcessHandle processHandle = ProcessHandle.current();

processHandle.getPid();
processHandle.isAlive();
processHandle.children().count();
processHandle.supportsNormalTermination();

ProcessHandle.Info processInfo = processHandle.info();

processInfo.arguments();
processInfo.command();
processInfo.totalCpuDuration();
processInfo.user();

// all Processes
Stream processStream = ProcessHandle.allProcesses();

// destroy Process
processHandle.destroy();

>> More details at: Java 9 Process API

2. Platform Logging API and Service

Java 9 defines a minimal logging API which platform classes can use to log messages, together with a service interface for consumers of those messages.

An implementation of LoggerFinder is loaded with help of java.util.ServiceLoader API using system class loader. Basing on this implementation, an application/framework can plug in its own external logging backend, without configuring java.util.logging or that backend.

We can pass the class name or module (related to specific Logger) to the LoggerFinder so that the LoggerFinder can know which logger to return.

If no concrete implementation is found, JDK default LoggerFinder implementation will be used. java.util.logging (in java.logging module) now becomes backend. So log messages will be routed to java.util.logging.Logger.

We obtain loggers that are created from the LoggerFinder using factory methods of the System class:

package java.lang;
...
public class System {
    System.Logger getLogger(String name) { ... }
    System.Logger getLogger(String name, ResourceBundle bundle) { ... }
}

>> More details and example at: Java 9 Platform Logging API and Service

3. CompletableFuture API Enhancements

To improve Java Future, Java 8 provides CompletableFuture which can execute some code whenever its ready. Now Java 9 improves CompletableFuture API that supports delay and timeout.

future.completeAsync(supplier, CompletableFuture.delayedExecutor(3, TimeUnit.SECONDS))
		.thenAccept(result -> System.out.println("accept: " + result));

// other statements
// TIMEOUT = 3;
// doWork() takes 5 seconds to finish

CompletableFuture future = 
		doWork("JavaSampleApproach")
		.orTimeout(TIMEOUT, TimeUnit.SECONDS)
		.whenComplete((result, error) -> {
			if (error == null) {
				System.out.println("The result is: " + result);
			} else {
				System.out.println("Sorry, timeout in " + TIMEOUT + " seconds");
			}
		});
// TIMEOUT = 3;
// doWork() takes 5 seconds to finish

CompletableFuture future = 
		doWork("JavaSampleApproach")
		.completeOnTimeout("JavaTechnology", TIMEOUT, TimeUnit.SECONDS)
		.whenComplete((result, error) -> {
			if (error == null) {
				System.out.println("The result is: " + result);
			} else {
				// this statement will never run.
				System.out.println("Sorry, timeout in " + TIMEOUT + " seconds.");
			}
		});

>> More details at: Java 9 CompletableFuture API Improvements – Delay and Timeout Support

4. Reactive Streams – Flow API

Java 9 introduces Reactive Streams under java.util.concurrent.Flow that supports an interoperable publish-subscribe framework. At a glance:

@FunctionalInterface
public static interface Publisher {
	public void subscribe(Subscriber subscriber);
}

public static interface Subscriber {
	public void onSubscribe(Subscription subscription);
	public void onNext(T item);
	public void onError(Throwable throwable);
	public void onComplete();
}

public static interface Subscription {
	public void request(long n);
	public void cancel();
}

public static interface Processor extends Subscriber, Publisher {
}

The diagram below shows its behavior and how to implement Reactive Stream with new Flow API:
reactive-stream-flow-interface-behavior

>> More details at:
Java 9 Flow API – Reactive Streams
Java 9 Flow API example – Publisher and Subscriber
Java 9 Flow API example – Processor
Java 9 FLow SubmissionPublisher – A Concrete Publisher

5. Factory Method for Collections: List, Set, Map

Java 9 provides new static factory methods for creating instances of collections and maps conveniently with small number of elements.

List immutableList = List.of("one", "two", "three");
Set immutableSet = Set.of("one", "two", "three");
Map immutableMap = Map.of(1, "one", 2, "two", 3, "three");

But the collections created with static factory method are immutable, so if we try to add/put more elements or null into them, we will get java.lang.UnsupportedOperationException or java.lang.NullPointerException.

>> More details at: Java 9 Factory Method for Collections: List, Set, Map

6. Enhanced Deprecation

There are new added methods of Java 9 @Deprecated annotation: forRemoval() and since().

An example with new @Deprecated annotation:
@Deprecated(since ="1.5", forRemoval = true)

>> More details at: Java 9 @Deprecated Enhancements

7. Stack-Walking API

Java 9 provides an efficient way of stack walking for lazy access, filtering stack trace with StackWalker.

StackWalker object allows us to traverse and access to stacks. It contains some useful and powerful methods:

public  T walk(Function, ? extends T> function);
public void forEach(Consumer action);
public Class getCallerClass();

The most important method is walk() that helps:
+ open a StackFrame stream for the current thread.
+ then apply the function with that StackFrame stream.

>> More details at: Java 9 StackWalker

8. Other Improvements

8.1 Stream Improvements

Java 9 Stream comes with some small useful improvements for Asynchronous Programming with new added methods: iterate(), takeWhile()/dropWhile(), ofNullable().

IntStream
	.iterate(1, i -> i < 20, i -> i * 2)
	.forEach(System.out::println);
//for ordered Stream
Stream.of(1, 2, 3, 4, 5, 6).takeWhile(i -> i <= 3).forEach(System.out::println);

// The result is:
// 1
// 2
// 3

// for unordered Stream
Stream.of(1, 6, 5, 2, 3, 4).takeWhile(i -> i <= 3).forEach(System.out::println);

// The result is:
// 1
//for ordered Stream
Stream.of(1, 2, 3, 4, 5, 6).dropWhile(i -> i <= 3).forEach(System.out::println);

// It drops (1,2,3), the result is:
// 4
// 5
// 6

//for unordered Stream
Stream.of(1, 6, 5, 2, 3, 4).dropWhile(i -> i <= 3).forEach(System.out::println);

// It drops (1), the result is:
// 6
// 5
// 2
// 3
// 4
// numbers [1,2,3,null]
// mapNumber [1 - one, 2 - two, 3 - three, null - null]

List newstringNumbers = numbers.stream()
		.flatMap(s -> Stream.ofNullable(mapNumber.get(s)))
		.collect(Collectors.toList());

// The result is:
// [one, two, three]

>> More details at: Java 9 Stream Improvements

8.2 Optional Improvements

Java 9 provides new Optional::stream to work on Optional objects lazily, it returns a stream of either zero or one/more elements. It also checks empty element automatically and removes it.

// streamOptional(): [(Optional.empty(), Optional.of("one"), Optional.of("two"), Optional.of("three")]

List newStrings = streamOptional()
				.flatMap(Optional::stream)
				.collect(Collectors.toList());
				
// Result: newStrings[one, two, three]

Instead of using isPresent() and orElse() to make code more clearlier and handle "else" case, now we have ifPresentOrElse() method with Java 9:

Optional result3 = getOptionalEmpty();
result3.ifPresentOrElse(
		x -> System.out.println("Result = " + x),
		() -> System.out.println("return " + result2.orElse(-1) + ": Result not found."));
		
// return -1: Result not found.

or() method checks if a value is present, it will return an Optional for the value, otherwise return another Optional which is produced by the supplying function.

Optional result = getOptionalEmpty() // Empty Optional object
		.or(() -> getAnotherOptionalEmpty()) // Empty Optional object
		.or(() -> getOptionalNormal())  // this return an Optional with real value 42
		.or(() -> getAnotherOptionalNormal());  // this return an Optional with real value 99
		
// Result: Optional[42]

>> More details at: Java 9 Optional Improvements

V. Client Technologies

1. Multi-Resolution Images

The new API which is defined in the java.awt.image package can help us:
– Encapsulate many images with different resolutions into an image as its variants.
– Get all variants in the image.
– Get a resolution-specific image variant – the best variant to represent the logical image at the indicated size based on a given DPI metric.

>> More details at: Java 9 Multi-Resolution Images

2. TIFF Image I/O Plugins

In earlier version of Java, Image I/O Framework javax.imageio provides a standard way to plug-in image codecs for some formats such as PNG and JPEG. But TIFF is still missing from this set. It was packaged in com.sun.media.imageio.plugins.tiff before. Java 9 TIFF Image I/O plugins has a new package called javax.imageio.plugins.tiff which is renamed from com.sun.media.imageio.plugins.tiff.

The package contains some classes that support the built-in TIFF reader and writer plug-ins. It includes:
- Some classes representing common additional tags and the set of tags found in baseline TIFF specification, Exif IFD, TIFF-F (RFC 2036) file, GeoTIFF IFD.
- TIFFImageReadParam: an extension of ImageReadParam which can specify which metadata tags are allowed to be read and set some destination properties.

>> More details at: Java 9 TIFF Image I/O plugins

VI. Internationalization

1. Unicode 8.0

Java 8 supported Unicode 6.2.
Java 9 now supports up to Unicode 8.0 standards with 10,555 characters, 29 scripts, and 42 blocks.

2. UTF-8 Properties Files

In previous releases, ISO-8859-1 encoding was used when loading property resource bundles (PropertyResourceBundle - constructing its instance from an InputStream requires that the input stream be encoded in ISO-8859-1). But using ISO-8859-1 is not a convenient way to represent non-Latin characters.

In Java 9, properties files are loaded in UTF-8 encoding.

If there is an issue, consider the following options:
- convert the properties file into UTF-8 encoding.
- specify the runtime system property:

java.util.PropertyResourceBundle.encoding=ISO-8859-1

>> More details at: Java 9 Internationalization Enhancements

3. Default Locale Data Change

In JDK 8 and previous releases, JRE is the default locale data. JDK 9 sets CLDR (the locale data provided by the Unicode Common Locale Data Repository project) as highest priority by default.

This is how we select locale data source in the preferred order using java.locale.providers system property. If a provider is failed to request locale data, the next provider will be processed:

java.locale.providers=COMPAT,CLDR,HOST,SPI

If we don't set the property, default behaviour is:

java.locale.providers=CLDR,COMPAT,SPI

>> More details at: Java 9 Internationalization Enhancements


Latest Posts: Java 9