Main Tutorials

What is new in Java 18

Java 18 logo

Java 18 reached general availability on 22 March 2022, download Java 18 here.

Java 18 has 9 JEP items.

Java 18 developer features.
No new developer features, mainly preview and incubator, Pattern Matching for switch (second preview), Vector API (Third Incubator) and Foreign Function & Memory API (Second Incubator).

1. JEP 400: UTF-8 by Default

In Java 18, this JEP makes the default charset to UTF-8. However, we still allow configuring the default charset to others by providing the system property ‘file.encoding’.

In Java 18, if the file.encoding system property is COMPACT, the JVM uses Java 17 and an earlier algorithm to choose the default charset.


java -Dfile.encoding=COMPAT

If the file.encoding is UTF-8, the default charset will be UTF-8, a no-op value in Java 18.


java -Dfile.encoding=UTF-8

Long story short
Before Java 18, the default charset was environment-dependent, meaning the Java Virtual Machine (JVM) chooses the default charset during start-up, based on the run-time environment, like the operating system, the user’s locale, and other factors. For example, on macOS, the default charset is UTF-8; on Windows, the default charset is ‘windows-1252’ (if English locale).

Since the default charset is not the same from machine to machine, APIs that use the default charset may cause unwanted behaviors or errors, especially the IO APIs like java.io.FileReader and java.io.FileWriter.

Further Reading

2. JEP 408: Simple Web Server

This JEP provides a command-line tool, jwebserver to start a simple web server to serve static files, suitable for prototyping, ad-hoc coding, testing, and educational purpose, not for a production server.

By default, the jwebserver starts a simple web server at port 8000, and serves the static files from the current directory that start the command. Furthermore, it serves only HEAD and GET requests, and other requests will receive a 501 – Not Implemented or a 405 – Not Allowed response.

Terminal

  ./jwebserver

  Binding to loopback by default. For all interfaces use "-b 0.0.0.0" or "-b ::".
  Serving /Library/Java/JavaVirtualMachines/temurin-18.jdk/Contents/Home/bin and subdirectories on 127.0.0.1 port 8000
  URL http://127.0.0.1:8000/
  127.0.0.1 - - [10/Nov/2022:18:59:22 +0800] "GET / HTTP/1.1" 200 -

Access http://127.0.0.1:8000

directory listing image

The below command starts a static web server at port 8888.

Terminal

  # We can use `-p` option to start the web server at different port

  ./jwebserver -p 8888

The below command starts a static web server at port 8888 and serves files from the directory /Users/mkyong.

Terminal

  # We can use `-d` option to serve static files from a specified directory

  ./jwebserver -p 8888 -d /Users/mkyong

Further Reading

3. JEP 413: Code Snippets in Java API Documentation

Before Java 18, we used {@code ...} to include the source code snippets in the documentation like this:


/**
 * <pre>{@code
 *     int sum = widgets.stream()
 *                      .filter(w -> w.getColor() == RED)
 *                      .mapToInt(w -> w.getWeight())
 *                      .sum();
 * }</pre>
 */

The javadoc tool will render the body of the {@code ...} tag as HTML code. This method has some disadvantages, like the inability to do syntax highlighting, can’t to contain HTML markups, indentation issues, etc.

This JEP introduces the @snippet tag to allow developers to be more flexible in including source code snippets in the documentation.

Inline snippets
The generated documentation will render the body of the {@snippet ...} tag as HTML code. There is no need to escape special characters like < and>` with HTML entities.


/**
* The following code shows how to use {@code Optional.isPresent}:
* {@snippet :
* if (v.isPresent()) {
*     System.out.println("v: " + v.get());
* }
* }
*/

External snippets
This includes the source code snippet from a separate file.

A comment from ShowExample.java links the source code snippets from a separate file ShowOptional.java.

ShowExample.java

 public class ShowExample {
     /**
      * The following code shows how to use {@code Optional.isPresent}:
      * {@snippet file="ShowOptional.java" region="example"}
      */
     void test() {
       //...
     }
 }

Where ShowOptional.java is a file containing:


public class ShowOptional {
    void show(Optional<String> v) {
        // @start region="example"
        if (v.isPresent()) {
            System.out.println("v: " + v.get());
        }
        // @end
    }
}

The content of the requested @start and @end regions will include in the ShowExample.java documentation.


  if (v.isPresent()) {
      System.out.println("v: " + v.get());
  }

Further Reading

Refer to the official JEP 413 for more @snippet examples like syntax highlighting, replacing the string, etc.

4. JEP 416: Reimplement Core Reflection with Method Handles

The existing core reflection has three internal mechanisms for invoking methods and constructors. Adding new features to the core reflection may modify all three code paths. Which is costly.

  1. VM native methods
  2. Dynamically generated bytecode stubs
  3. Method handles

This JEP reimplements core reflection with method handles as the underlying reflective mechanism. There is no change to the java.lang.reflect API; solely an implementation change.

P.S We can enable back the old implementation via -Djdk.reflect.useDirectMethodHandle=false.

5. JEP 417: Vector API (Third Incubator)

This JEP improves the Vector API performance and other enhancements in response to feedback.

History

6. JEP 418: Internet-Address Resolution SPI

By default, the java.net.InetAddress API uses the operating system’s built-in resolver to resolve host names to Internet Protocol (IP) addresses.


InetAddress ip = InetAddress.getByName("google.com");

This JEP redesign java.net.InetAddress API to use service loader to find the resolver instead of using the operating system’s built-in resolver.

InetAddress.java

  private static InetAddressResolver loadResolver() {
      return ServiceLoader.load(InetAddressResolverProvider.class)
              .findFirst()
              .map(nsp -> nsp.get(builtinConfiguration()))
              .orElse(BUILTIN_RESOLVER);
  }

7. JEP 419: Foreign Function & Memory API (Second Incubator)

This Foreign Function & Memory API allows the developer to access the code outside the JVM (foreign functions), data stored outside the JVM (off-heap data), and accessing memory not managed by the JVM (foreign memory).

This JEP improves the Foreign Function & Memory API and other enhancements in response to feedback.

History

  • Java 14 JEP 370 introduced Foreign-Memory Access API (Incubator).
  • Java 15 JEP 383 introduced Foreign-Memory Access API (Second Incubator).
  • Java 16 JEP 389 introduced Foreign Linker API (Incubator).
  • Java 16 JEP 393 introduced Foreign-Memory Access API (Third Incubator).
  • Java 17 JEP 412 introduced Foreign Function & Memory API (Incubator).
  • Java 18 JEP 419 introduced Foreign Function & Memory API (Second Incubator).

P.S Please refer to previous Foreign Linker API examples in Java 16.

8. JEP 420: Pattern Matching for switch (Second Preview)

This JEP is the second preview of pattern matching for the switch, with the following enhancements since the first preview:

8.1. Dominance checking of the same type.

Review the below switch pattern matching, every value that matches the String s also matches the CharSequence cs, which makes String s unreadable and will cause a compile-time error.


  static void error (Object o){
        switch (o) {
            case CharSequence cs -> System.out.println("A sequence of length " + cs.length());
            case String s ->    // Error - pattern is dominated by previous pattern
                    System.out.println("A string: " + s);
            default -> {
                break;
            }
        }
    }

If we run the above code with Java 18, it will cause a compile-time error.

Terminal

    java: this case label is dominated by a preceding case label

The IntelliJ IDE will highlight the error label and show a more user-friendly error message when you hover over it.

Terminal

    Label is dominated by a preceding case label 'CharSequence cs'

8.2 Exhaustiveness of switch expressions and statements

The switch expression requires all possible values to be handled in the switch block, else prompts a compile-time error.

Review the below code:


    static int coverage(Object o) {
        return switch (o) {         // Error - not exhaustive
            case String s  -> s.length();
            case Integer i -> i;
        };
    }

If we run the above code with Java 18, it will cause a compile-time error.

Terminal

  java: the switch expression does not cover all possible input values

The below code is fine because the default will handle all the possible types.


    static int coverage(Object o) {
        return switch (o) {
            case String s  -> s.length();
            case Integer i -> i;
            default -> 0;
        };
    }

History

  • Java 17 JEP 406 introduced Pattern Matching for switch (Preview).
  • Java 18 JEP 420 introduced Pattern Matching for switch (Second Preview).

P.S Please refer to the previous Pattern Matching for switch examples in Java 17.

9. JEP 421: Deprecate Finalization for Removal

The object finalization is unpredictable and dangerous; read this and this.

Object.java

  // deprecated since Java 9
  @Deprecated(since="9", forRemoval=true)
  protected void finalize() throws Throwable { }

This JEP deprecates (again?) finalization for removal in the future release. In Java 18, we can use the command-line option --finalization=disabled to disable finalization. The finalization remains enabled by default;

Review the below try-finally code; if the copy throws an exception, or the output.close(); throws an exception, then the input will never close and cause the resource to leak.

JEP421.java

package com.mkyong.java18.jep421;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class JEP421 {

  public static void main(String[] args) throws IOException {

      String file1 = "/Home/mkyong/file1";
      String file2 = "/Home/mkyong/file1";

      FileInputStream input = null;
      FileOutputStream output = null;
      try {
          input = new FileInputStream(file1);
          output = new FileOutputStream(file2);

          // copy files from file1 to file 2

          output.close();
          output = null;
          input.close();
          input = null;
      } finally {
          if (output != null) output.close();
          if (input != null) input.close();
      }

  }

}

The better solution is to use Java 7 try-with-resources to properly open and close the resources.


      try (FileInputStream input = new FileInputStream(file1);
           FileOutputStream output = new FileOutputStream(file2)) {
          // copy files from file1 to file 2
      }

Download Source Code

$ git clone https://github.com/mkyong/core-java

$ cd java-18

References

About Author

author image
Founder of Mkyong.com, love Java and open source stuff. Follow him on Twitter. If you like my tutorials, consider make a donation to these charities.

Comments

Subscribe
Notify of
0 Comments
Inline Feedbacks
View all comments