Changes in Java 9 - ashishranjandev/developer-wiki GitHub Wiki

New Changes in Java 9

Major changes

  • Language
  • Compiler
  • JVM
  • Tooling

Module System - Modularluse the JDK - Why?

  • Make JDK maintainable again
  • Modularize applications
  • Optional

What was issue?

rt.jar i.e. Runtime JAR -> 10,000 classes entangled

graph TD
    JavaPrefs --> java.xml
    java.xml --> java.base
    java.sql --> java.xml
    java.logging --> java.base
Loading

Cyclic Dependency is not supported.

Advantages:

  • Increased Security
  • Reduced footprint
  • Easy deprecation
  • Future Proof
  • easy to skip
  • Easy to experiment like jdf.incubate.httpclient in Java 9

What is module?

  • name, self contained, groups related tool
  • high level grouping construct
  • has everything needed to execute
  • Are jar files modules? No. Because there are no clearly defined boundaries.

Module JDK - Encapsulation

Module Name. | java.base |

-------------------------
public exposed.| java.logs |
public exposed.| java.util |
-------------------------
private, encapsulated | |
(not accessible outside). | |
-------------------------
module java.base {
exports java.lang; 
exports java.utils; 
---------> no requires, means there are no dependencies <br/>
}
module java.sql {
exports javax.sql; 
exports javax.transaction.xa; 
requires java.logging;
requires java.xml;
}

export takes package name
requires take module name

java --list-modules

[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]

java --describe-module java.sql

exports java.sql
exports javax.sql
requires java.base mandated
requires java.logging transitive
requires java.xml transitive
requires java.transaction.xa transitive
uses java.sql.Driver

Migrating to Module based Architecture

javac -cp $classPath
java -cp $classPath

No issue unless

  1. Use Encapsulated types
  2. Using non-default modules

Secenarios

Code using internal APIs or Encapulated
  • Compiles with Java 8 with warning
  • Runs in Java 9
  • Compilation fails in Java 9 -> Use Jdeps for suggesting replacement
  • Run with Java 9 also fails with --illegal-access=deny -> This will fail
  • To run use java --illegal-access=permit -> Shows warning
  • javac --add-exports java.base/sun.security.X509=ALL-UNNAMED
  • jdeps -jdkinternals Main.class
Importing packages outside from java se

import javax.xml.bind.DatatypeConverter -> Java 9 Compile -> package does not exist

Default: Only modules reachable from java.se accessible.

module graph

Why java.se.ee not included?

Most java application servers ship with their own implementations of the ee APIs. So to avoid confict.

javac --add-modules java.xml.bind
java --add-modules java.xml.bind 

Main class loadded as part of unnamed module.

Use jdeps >> jdeps Main.class

Collection changes

Creating a list with 3 elements

Java 8 way

List<String> books = new ArrayList<>();
        books.add("Java Programming");
        books.add("Data Structures and Algorithms");
        books.add("Design Patterns");

// Alterative 

List<String> books = Arrays.asList("Java Programming", "Data Structures and Algorithms", "Design Patterns");
// Anonymous Instance collection Type

//
new ArrayList<>() {{ add("Java 9 mad"); }}
Collections.emptyList();

Java 9 way

List.of(1, 2, 3);
// Intermediate Array Allocation

Set.of("first", "second");
// Null not allowed
// Duplicates not allowed

JShell

REPL for Java -> Read Eval Print logs -> like Lisp, Groovy, Scala

Why?

  • Quickly test Ideas
  • Exploring APIs -> Code completion, built in doc
  • Teaching tool
$ jshell

jshell> 1 + 1
$1 ==> 2

// No semicolon is required
// What is $1 -> Identifier that can be used later.

jshell> "abc".matches("[ab]+b\\s")

jshell> /save mysession.jsh
jshell> /open mysession.jsh. -> print will happen again

jshell --class-path   -> Sets class path
jshell --class-path commons-lang-3.5.jar. -> explore libraries
Jshell jshell = jshell.create();
List<SnippetEvent> events = jshell.eval("int i = 0;")
Stream<SnippetEvent> vars = jshell.variables();
Map.of("Key 1", 1, "Key 2", 2) -> {Key 1= 1, Key 2 = 2}
Map.ofEntries(Map.entry("key 1", true), Map.entry("key2", false))

// Iteration Order is not guranteed for Map.Sets

Streams

They are ephimermal. It can have infinite elements.

Improvements

  • takewhile
  • dropwhile
  • ofNullable() -> For 0 or 1
  • iterate(T seed, Predicate<> hasNext, Unary next). -> Predicate has been added.

Sample Stream - 0,0,0,1,1,2,2,2

  • takeWhile(is 0) -> 0,0,0. will be taken.
  • dropWhile(is 0) -> 1,1,2,1,2 will be taken.
  • Works well for Ordered Streams

New Collectors -> Collectors -> Materialise stream into new collection

Filtering
Collector<...> filtering(Predicate<..> predicate, Collector<..> downstream)

book.collect(groupingBy(Book::getAuthors, filtering(b -> b.getPrice() > 10, toSet())));
Optional -> 1 value or null value, supports Transformers

Existing

Optional.of("Hi")

Optional.empty()

s.map(String::toUpperCase) // "Hi"
i.map(n -> n + 1) -> Empty option, no NPE

Addition to Optionals

public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction);

public Stream<T> stream();

public Optional<T> or(Supplier<? extends Optional<? extends T>> supplier);
        // Before ifPresentOrElse
        full.ifPresent(System.out::println);
        if (full.isPresent()) {
            System.out.println(full.get());
        } else {
            System.out.println("Nothing here");
        }

        // After Java 9
        full.ifPresentOrElse(System.out::println,
                () -> System.out.println("Nothing here!"));

Interoperability between optional and streams. Now we can get a stream of 1 and 2.

        Stream<Optional<Integer>> optionals =
                Stream.of(Optional.of(1), Optional.empty(), Optional.of(2));

        Stream<Integer> ints = optionals.flatMap(Optional::stream);

        ints.forEach(System.out::println);

        // Find all first authors of the books
        Set<String> authors = Book.getBooks()
                .map(book -> book.authors.stream().findFirst())
                .flatMap(optAuthor -> optAuthor.stream())
                .collect(Collectors.toSet());
        System.out.println(authors);

Chaining of Optional

        // Java 8
        Book bestBookBefore = getBestOffer()
                .orElse(getExternalOffer().orElse(localFallback.get()));  // .get() is BAD!

        Optional<Book> bestBook =
                getBestOffer()
                .or(() -> getExternalOffer())
                .or(() -> localFallback);

Other Changes

Single Underscore is not a valid identifier -> Possible use might be lambda
Try with Resources

Before java 9 only the resources declared at the start would get closed. However, If the resource is declared before the start it would not get closed

// Java 8
public void normalTryWithResources() throws IOException {
        try (FileInputStream fis = new FileInputStream("~/tmp/test")) {
           fis.read();
        }
    }

// fis = null; // Re-assignment makes fis not 'effectively final'
        try (FileInputStream fis2 = fis) {
            fis2.read();
        }
// Java 9
public void doWithFile(FileInputStream fis) throws IOException {
        // Only if fis is 'effectively final', can this form be used
        // fis has to be effectively final
        try (fis) {
            fis.read();
        }

    }
Generic Class
ArrayList<String> list1 = new ArrayList<String>();
        ArrayList<String> list2 = new ArrayList<>();
        ArrayList<String> list3 = new ArrayList<>() {
            @Override
            public boolean add(String s) {
                System.out.println("Adding " + s);
                return super.add(s);
            }
        };
Private Interface Methods

For common logic in Interfaces

    double getPrice();

    default double getPriceWithTax() {
        return getPriceWithTaxInternal();
    }

    default double getPriceWithDiscount() {
        return getPriceWithTaxInternal() * 0.80;
    }

    private double getPriceWithTaxInternal() {
        return getPrice() * 1.20;
    }
Other Improvements
  • Java Doc is HTML 5 Compliant
  • Search added in Java Docs
  • Modules included in Java Docs
  • Support of Unicode upgraded from 6.2 to 8.0
  • Properties file now supports UTF-8 and not ISO-8859-1 -> No effect if only ASCII characters are there.
  • For Locale it uses Common Locale Data Repository(public data set describing localisation formats). Previously Java had its own home brew formats. -> For no impact, in java.locale.providers put COMPAT in front of CLDR
Date

Added in Time

  • long dividedBy(Duration divisor)
  • Duration truncatedTo(TemporalUnit unit)
  • static Clock systemUTC()

Added in Clock

  • static Clock systemUTC()
        LocalDate birthDay = LocalDate.of(1991, 12, 6);

        System.out.println(birthDay.
                                datesUntil(LocalDate.now(), Period.ofYears(1)).
                                map(date -> Year.of(date.getYear())).
                                filter(Year::isLeap).
                                count());
Process Handlers
  1. java.lang.Process - Represents all the process created by Java
  2. java.lang.ProcessHandler - Represent any native process on the operating system
classDiagram
  class Process {
    +start(): ProcessHandle
    +destroy(): void
    +isAlive(): boolean
+toHandle(): ProcessHandle
  }

  class ProcessHandle {
    +info(): ProcessHandleInfo
    +onExit(): CompletableFuture<ProcessHandle>
    +destroy(): void
+info(): ProcessHandleInfo
  }

  class ProcessHandleInfo {
    +pid(): long
    +command(): String
    +arguments(): String[]
    +user(): String
  }

  JavaProcess --> ProcessHandle
  ProcessHandle --> ProcessHandleInfo
Loading
long pidOld = Long.parseLong(ManagementFactory.getRuntimeMXBean().getName().split("@")[0]);
        // With the new ProcessHandle API in Java 9:
        long pidNew = ProcessHandle.current().pid();
        long parentPid = ProcessHandle.current().parent().get().pid();

ProcessHandle textEditHandle =
          ProcessHandle.allProcesses()
            .filter(h -> h.info().commandLine().map(cmd -> cmd.contains("TextEdit")).orElse(false))
            .findFirst()
            .orElseThrow(() -> new IllegalArgumentException("No matching handle found"));
HTTP Client
HTTP 2 HTTP 1
Request/Response Request/Response
GET/PUT/POST GET/PUT/POST
Binary Protocol Text Protocol
Mandatory TLS Optional TLS
Multiplexing over same TCP Connection No Multiplexing
Server Push Capability No Server Push Capability
HttpClient.Builder builder = HttpClient.newBuilder();

        builder.version(HttpClient.Version.HTTP_2)
                .followRedirects(HttpClient.Redirect.ALWAYS);

        HttpClient client = builder.build();

        HttpRequest request = HttpRequest.newBuilder(URI.create("https://www.google.com"))
                .header("User-Agent", "Java")
                .timeout(Duration.ofMillis(500))
                .GET()
                .build();

        CompletableFuture<HttpResponse<String>> response =
        client.sendAsync(request, HttpResponse.BodyHandlers.ofString());

        response.thenAccept(r -> {
            System.out.println("Version: " + r.version());
            System.out.println(r.body());
        });

        response.join();
Reactive Streams
  • BackPressure Support for Stream Data
  • Vendor Neutral Specification
  • java.util.Concurrent - Flow API - Only Interfaces
  • Interoperability with Akka Streams, Rx Java 2, Spring 5
  • Not an end user API
sequenceDiagram
  participant Publisher as Publisher
  participant Subscription as Subscription
  participant Subscriber as Subscriber

  Publisher ->> Subscription: subscribe
  Subscription ->> Subscriber: onSubscribe
  Subscriber ->> Subscription: request
  Subscription ->> Subscriber: onNext
  Subscriber ->> Subscription: onComplete
  Subscriber ->> Subscription: onError
  Subscription ->> Publisher: cancel

Loading
Stack Walker
  • Low Performance
  • No Partial Handling
classDiagram
  class StackWalker {
    +walk(): Stream<StackWalker.StackFrame>
    +getCallerClass(): Class<?>
  }

  class StackWalker.StackFrame {
    -className: String
    -methodName: String
  }

  class StackWalker.Option {
    -SHOW_REFLECT_FRAMES: StackWalker.Option
    -SHOW_HIDDEN_FRAMES: StackWalker.Option
  }

  StackWalker --> StackWalker.StackFrame
  StackWalker --> StackWalker.Option

Performance Improvements

GC
  • CMS - Deprecated
  • G1GC - Default GC in Server mode (Introduced in Java 6)

Generation garbage collectors divided the Heap

  • Eden
  • Survivor Region
  • Tenured

Issues

  • Stop the world GC Pause
  • Too many configurations

What is new

  • Instead of dividing in 3 continous regions - Heap divided into multiple regions of 32 MB
  • Now G1 GC works incrementally -low pause time
  • GC of Eden and Survivor region can happen in parallel
  • parallel marking phase
  • Tuneable pause time, low pause time
  • (-) Parallel GC threads - takes up CPU time
  • (-) Trade throughput for latency
  • Automated tuning
  • this is especially beneficial for large heaps
  • It has pause time interval, so that GC does not happen very often
  • Faster memory management with low tuning
-XX:MaxGCPauseMillis=200
## Not enabled by default
-XX:+G1EnableStringDeduplication 
String Performance Improvements
  • Compact Strings
  • Concatenation
Compact Strings

Before Java 9

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

This char[] used to store - Ashish as |A|0|s|0|i|0|s|h|0|
Default encoding is UTF-16. Hence for (most) string with only ASCII values we were using double storage in heap.

From Java 9

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {

    @Stable
    private final byte[] value;

    /**
     * The identifier of the encoding used to encode the bytes in
     * {@code value}. The supported values in this implementation are
     *
     * LATIN1
     * UTF16
     */
    private final byte coder;

The code byte will tell us if value contains LATIN 1 or UTF 16. Hence for latin characters we can save space.

Since Strings are major part of any Heap. This can free up around 10-15% of heap.

String concatenation
String s = "a" + "b" + "c";
// this gets translated to 
String s = new StringBuilder;
s.append("a");
s.append("b");
s.append("c");

However if we were to improve it, it would need recompiling.

  • Hence Java has marked this as Invokedynamic byte code.
  • There will be a late binding to actual implementation.
  • Stable bytecode, where future improvement is possible. This was used for lambdas as well.
Java Serialisation
sequenceDiagram
  participant Java as Java
  participant Raw_Data as Raw_Data

Java ->> Raw_Data: ObjectOutputStream
Raw_Data ->> Java: ObjectInputStream

Loading

the second step is security nightmare as any input can become executable java code.

public interface ObjectInputFilter {

    /**
     * Check the class, array length, number of object references, depth,
     * stream size, and other available filtering information.
     * Implementations of this method check the contents of the object graph being created
     * during deserialization. The filter returns {@link Status#ALLOWED Status.ALLOWED},
     * {@link Status#REJECTED Status.REJECTED}, or {@link Status#UNDECIDED Status.UNDECIDED}.
     *
     * @param filterInfo provides information about the current object being deserialized,
     *             if any, and the status of the {@link ObjectInputStream}
     * @return  {@link Status#ALLOWED Status.ALLOWED} if accepted,
     *          {@link Status#REJECTED Status.REJECTED} if rejected,
     *          {@link Status#UNDECIDED Status.UNDECIDED} if undecided.
     */
    Status checkInput(FilterInfo filterInfo);
}

checkInput is used before the bytes are actually used by using

  • Instance Method - ObjectInputSteam::setObjectInputFilter
  • All Streams - ObjectInputFilter.Config.setSerialFilter
  • (Backported till Java 6)jdk.serialFilter - maxbytes,maxarrays,maxdepth or packages (allowed or blocked).

TLS

Introduction to ALPN : Application L

Now Application Layer protocol can be selected during TLS handshake.
Earlier ALP negotiation used to happen after the TLS handshake.

sequenceDiagram
  participant Client as Client
  participant Server as Server

Client ->> Server: Client Hello with Protocol
Server ->> Client: Server Hello with Selected protocol

Loading

This is required for HTTP/2.

DTLS - Datagram TLS

TLS is designed for reliable communications like TCP. However DTLS is for unsecured comm. No order no acknowledgement.

Java 9 supports DTLS 1.0,1.2 -> along with TLS 1.1, 1.2 through SSL Engine and Datagram socket.

OCSP Stapling : Online Certificate Status Protocol

Even if the server certificate is valid. It could be revoked by authority. Hence the application has to check the server status with OCSP. This could overwhelm OCSP server.

sequenceDiagram
  participant Client as Client
  participant Server as Server
participant OCSP_Server as OCSP_Server

Client ->> Server: Start TLS Connection
Server ->> OCSP_Server : Verify Certificate Status - with timestamp till what time response can be cached
Server ->> Server : Cache the OCSP response
Server ->> Client: return stapled certificate - with timestamp till what time Cert can be trusted
Loading
SHA Certificates are disabled

An attack was found on this hash function that allows attackers to create deliberate hash collisions, that's bad. In response to this, certificates that are still signed using the SHA-1 hash function are now rejected by default in Java 9.

default rejection only applies to certificates that chain back to a root certificate that is part of the default trusted root certificate authorities in the JDK. If you use your own local or enterprise root certificates with the SHA-1 hash, those are not affected. They will still be accepted. Of course, you shouldn't use those, and if you do, you should move to a more secure certificate. The good news is that in Java 9 support is added for the SHA-3 family of hash functions, which are currently the state of the art in standardized secure hashing

⚠️ **GitHub.com Fallback** ⚠️