© Fu Cheng 2018

Fu Cheng, Exploring Java 9, https://doi.org/10.1007/978-1-4842-3330-6_16

16. Miscellaneous

Fu Cheng

(1)Auckland, New Zealand

This chapter covers various changes in Java 9.

Small Language Changes

Java 9 has added some small language changes.

Private Interface Methods

It’s now possible to add private methods to interfaces in Java 9. The interface SayHi in Listing 16-1 has the private method buildMessage() to generate the default message to use in the default method sayHi().

Listing 16-1. Private Interface Methods
public interface SayHi {
  private String buildMessage() {
    return "Hello";
  }


  void sayHi(final String message);

  default void sayHi() {
    sayHi(buildMessage());
  }
}

Resource References in try-with-resources

The try-with-resources statement introduced in Java 7 is a small yet useful improvement in the Java language. In Java 9, it’s been improved again to allow you to use effectively-final variables, so you no longer need to declare fresh variables for resources managed by this statement.

In Listing 16-2, the variable inputStream is effectively-final, so it can be used in the try-with-resources statement. The same code cannot compile in Java 8.

Listing 16-2. try-with-resources Statement with effectively-final Variables
public void process() throws IOException {
  InputStream inputStream = Files.newInputStream(Paths.get("test.txt"));
  try (inputStream) {
    inputStream.readAllBytes();
  }
}

Other Changes

There are a few other changes in Java 9:

  • The underscore (_) cannot be used as an identifier. Doing so now causes a compile error.

  • @SafeVarargs can now be used on private instance methods.

The Stack-Walking API

java.lang.StackWalker is the new class in Java 9 to traverse stack traces that supports filtering and lazy access.

You start by creating new instances of StackWalkerusing the static method getInstance(). When using getInstance(), you can pass one or more options defined in the enum StackWalker.Option; see Table 16-1. You can also pass an estimate depth of the traverse.

Table 16-1. Options of getInstance()

Option

Description

RETAIN_CLASS_REFERENCE

Retains the Class object in the StackFrame

SHOW_HIDDEN_FRAMES

Shows all hidden frames

SHOW_REFLECT_FRAMES

Shows all reflection frames

After you get an instance of StackWalker, you can traverse the stack frames of the current thread using the method <T> T walk(Function<? super Stream<StackWalker.StackFrame>,? extends T> function). The only parameter is a function that applies to a stream of StackWalker.StackFrames and returns a value. When the method walk() returns, the stream is closed and cannot be used again. StackWalker.StackFrame has different methods for retrieving information about the stack frame; see Table 16-2.

Table 16-2. Methods of StackWalker. StackFrame

Method

Description

String getClassName()

Returns the binary name of the declaring class of the method

String getMethodName()

Returns the name of the method

Class<?> getDeclaringClass()

Returns the declaring Class of the method

int getByteCodeIndex()

Returns the index to the code array of the Code attribute containing the execution point

String getFileName()

Returns the name of the source file

int getLineNumber()

Returns the line number of the source line

boolean isNativeMethod()

Returns true if the method is native

StackTraceElement toStackTraceElement()

Returns the StackTraceElement

If the option RETAIN_CLASS_REFERENCE is not passed when you create the StackWalker, the method getDeclaringClass() throws UnsupportedOperationException.

In Listing 16-3, I create a StackWalker with the option RETAIN_CLASS_REFERENCE, so I can use the method getDeclaringClass() to get the Class object. Here I use getClassName() to get the class name and collect them into a Set. I verify that the set should contain the current class StackWalkingTest.

Listing 16-3. Example of walk() in StackWalker
public class StackWalkingTest {

  @Test
  public void testWalkClass() throws Exception {
    final StackWalker stackWalker =
        StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE);
    final Set<String> classNames = stackWalker.walk(stream ->
        stream.map(StackWalker.StackFrame::getClassName)
            .collect(Collectors.toSet())
    );
    assertTrue(classNames.contains("StackWalkingTest"));
  }
}

If I just need to iterate all the StackFrames and perform actions on each of them, the method void forEach(Consumer<? super StackWalker.StackFrame> action) is easier to use than walk(); see Listing 16-4.

Listing 16-4. Example of forEach() in StackWalker
@Test
public void testForEach() throws Exception {
  final StackWalker stackWalker =
      StackWalker.getInstance(Set.of(Option.SHOW_HIDDEN_FRAMES,
          Option.SHOW_REFLECT_FRAMES));
  stackWalker.forEach(stackFrame -> System.out.printf("%6d| %s -> %s %n",
      stackFrame.getLineNumber(),
      stackFrame.getClassName(),
      stackFrame.getMethodName()));
}

StackWalker also has the method getCallerClass() that returns the Class object of the caller who invoked the method that invoked getCallerClass(). The description of getCallerClass() seems quite complicated and hard to understand. This method is actually very useful for utility methods that deal with classes, class loaders, and resources. Those utility methods should use the caller’s class to perform actions.

LoggerManager in Listing 16-5 creates new System.Logger instances. The names of these loggers are based on the caller class of the method getLogger(). The option RETAIN_CLASS_REFERENCE must be configured for getCallerClass() to work. getCallerClass() always ignores reflection frames and hidden frames.

Listing 16-5. LoggerManager
public class LoggerManager {

  private final static StackWalker walker =
      StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE);


  public static System.Logger getLogger() {
    final Class<?> caller = walker.getCallerClass();
    return System.getLogger(
        String.format("%s/%s", caller.getModule().getName(), caller.getName()));
  }
}

In Listing 16-6, I create a new logger using LoggerManager.getLogger() and verify its name. Because the test class is in the unnamed module, the module name is null. StackWalkingTest is the class name. The logger name is null/StackWalkingTest.

Listing 16-6. Test LoggerManager
@Test
public void testGetCallerClass() throws Exception {
  final System.Logger logger = LoggerManager.getLogger();
  assertEquals("null/StackWalkingTest", logger.getName());
  logger.log(Level.INFO, "Hello World");
}

Objects

In Java 9, more methods have been added to java.util.Objects:

  • T requireNonNullElse(T obj, T defaultObj): Returns the first argument if it’s non-null, otherwise returns the second non-null argument.

  • T requireNonNullElseGet(T obj, Supplier<? extends T> supplier): Returns the first argument if it’s non-null, otherwise uses the Supplier to get the non-null value.

  • int checkIndex(int index, int length): Checks if index is within the bounds of range from 0 (inclusive) to length (exclusive). It can be used to check for array access. This method returns index if it’s in the range, otherwise the method throws IndexOutOfBoundsException.

  • int checkFromIndexSize(int fromIndex, int size, int length): Checks if the subrange from fromIndex (inclusive) to fromIndex + size (exclusive) is within the bounds of the range from 0 (inclusive) to length (exclusive). It can be used to check for subarray access. This method returns fromIndex if the subrange is in the range, otherwise the method throws IndexOutOfBoundsException.

  • int checkFromToIndex(int fromIndex, int toIndex, int length): Checks if the subrange from fromIndex (inclusive) to toIndex (exclusive) is within the bounds of the range from 0 (inclusive) to length (exclusive). This method returns fromIndex if the subrange is in the range, otherwise the method throws IndexOutOfBoundsException.

Listing 16-7 shows some examples of these new methods.

Listing 16-7. Examples of Methods in Objects
public class TestObjects {

  @Test
  public void testRequireNonNullElse() throws Exception {
    assertEquals("hello", Objects.requireNonNullElse("hello", "world"));
    assertEquals("world", Objects.requireNonNullElse(null, "world"));
  }


  @Test(expected = IndexOutOfBoundsException.class)
  public void testCheckIndex() throws Exception {
    assertEquals(0, Objects.checkIndex(0, 1));
    Objects.checkIndex(3, 1);
    assertEquals(1, Objects.checkFromIndexSize(0, 2, 5));
    Objects.checkFromIndexSize(0, 3, 1);
    assertEquals(1, Objects.checkFromToIndex(1, 3, 5));
    Objects.checkFromToIndex(0, 3, 2);
  }
}

Unicode 8.0

Java 9 has upgraded its support of Unicode to 8. 0 ( www.unicode.org/versions/Unicode8.0.0/ ), which is two major versions upgraded from Unicode 6.2 in Java 8. In Unicode 8.0, you can use emojis ( www.unicode.org/emoji/ ). This upgrade also brings better text display for right-to-left languages, such as Arabic and Hebrew.

UTF-8 Property Resource Bundles

The resource bundles in the Java platform have been using the ISO-8859-1 encoding for properties files for a long time. Properties files need to be converted using the tool native2ascii ( https://docs.oracle.com/javase/8/docs/technotes/tools/unix/native2ascii.html ) to escape Unicode characters. It’s impossible to directly edit these properties files.

In Java 9, the default file encoding for ResourceBundle to load properties files has changed from ISO-8859-1 to UTF-8. Applications can use UTF-8 properties files directly without conversion. In Listing 16-8, I use ResourceBundle to load a properties file with Chinese characters.

Listing 16-8. UTF-8 Resource Bundles
public class UTF8ResourceBundleTest {

  @Test
  public void testGetProperty() {
    final ResourceBundle resourceBundle = ResourceBundle.getBundle("demo");
    assertEquals("你好", resourceBundle.getString("greeting"));
  }
}

You can override the default resource bundle encoding using the system property java.util.PropertyResourceBundle.encoding. The possible encodings are ISO-8859-1 and UTF-8.

Enhanced Deprecation

The annotation @Deprecated marks a program element as deprecated and as one that should not be used. @Deprecated is enhanced in Java 9 with the addition of two new attributes. The attribute since indicates the version in which the annotated element became deprecated. The version string has the same format as the javadoc tag @since. The default value of since is an empty string. The attribute forRemoval indicates whether the annotated element is subject to removal in a future version. The default value is false. If the attribute forRemoval is false, the annotated element will still exist, at least in the next major release. If the attribute forRemoval is true, the future version in which the annotated element will be removed is unclear. It could be the next major release, or even after several other major releases. @Deprecated and the javadoc tag @deprecated should always be present at the same time on deprecated elements.

With these two new attributes, it’s easier for developers to know which actions they should take when they’re using deprecated elements. If the forRemoval is true for a deprecated element, then they need to update code that uses this element. Otherwise, the code will break when the element is removed in a future version.

In Java 9, @Deprecated annotations in many places have been updated to include these two attributes. For example, in the class java.lang.Thread, the methods suspend(), resume(), and stop() are marked as @Deprecated(since="1.2"), which means these methods are deprecated since Java 1.2, but that they will still exist, at least in next major release. The methods countStackFrames(), destroy(), and stop(Throwable obj) are marked as @Deprecated(since="1.2", forRemoval=true), so these methods will be removed in a future version.

When deprecated elements are used, the compiler issues warnings. With the introduction of forRemoval, there are now two types of deprecation warning. The ordinary deprecation warnings are for @Deprecated with forRemoval set to false; the terminal deprecation warnings are for @Deprecated with forRemoval set to true. Before Java 9, you could use @SuppressWarnings("deprecation") to suppress the deprecation warnings. In Java 9, @SuppressWarnings("deprecation") can only suppress ordinary deprecation warnings. To suppress terminal deprecation warnings, you need to use @SuppressWarnings("removal").

You can use the tool jdeprscan to scan the usages of deprecated APIs. jdeprscan supports scanning a directory that is the root of a package hierarchy, a JAR file and a class file. By using the option --for-removal, you can limit the scanning results to be with APIs that are deprecated for removal. jdeprscan -l can print out the set of deprecated APIs.

The following command scans the JAR file of Guava:

$ jdeprscan <path>/guava-21.0.jar

Listing 16-9 shows the scanning result.

Listing 16-9. jdeprscan Scanning Result of Guava
class com/google/common/io/FileBackedOutputStream$1 overrides
  deprecated method java/lang/Object::finalize()V
class com/google/common/reflect/Element uses deprecated
  method java/lang/reflect/AccessibleObject::isAccessible()Z
class com/google/common/reflect/Element overrides deprecated
  method java/lang/reflect/AccessibleObject::isAccessible()Z
class com/google/common/util/concurrent/AtomicDoubleArray uses deprecated method
  java/util/concurrent/atomic/AtomicLongArray::weakCompareAndSet(IJJ)Z

NetworkInterface

The class java.net.NetworkInterface adds three new methods related to network interfaces:

  • Stream<NetworkInterface> networkInterfaces(): Static; returns a stream of NetworkInterfaces

  • Stream<InetAddress> inetAddresses(): Returns a stream of InetAddresses bound to this network interface

  • Stream<NetworkInterface> subInterfaces(): Returns a stream of subinterfaces that attached to this network interface

Listing 16-10 shows how to use these three new methods. It displays information of all network interfaces.

Listing 16-10. Usage of New Methods in NetworkInterface
public class NetworkInterfaceDemo {

  public static void main(final String[] args) throws SocketException {
    final String allInterfaces = NetworkInterface.networkInterfaces().map(
        networkInterface ->
            String.format("Name:%s%nHost addresses:%s%nSub-interfaces:%s",
                networkInterface.getDisplayName(),
                networkInterface.inetAddresses().map(InetAddress::getHostAddress)
                    .collect(Collectors.joining(", ")),
                networkInterface.subInterfaces().map(NetworkInterface::getName)
                    .collect(Collectors.joining(", ")))
    ).collect(Collectors.joining(" "));
    System.out.println(allInterfaces);
  }
}

Summary

As the last chapter of this book, this chapter covered changes in Java 9 that are too small to be in their own chapters, including small language changes, the stack-walking API, UTF-8 property resource bundles, enhanced deprecation, and other small changes.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.36.194