Java 9の新機能

Java 9の新機能

1. 概要

Java 9には豊富な機能セットが付属しています。 新しい言語の概念はありませんが、新しいAPIと診断コマンドは間違いなく開発者にとって興味深いものになります。

この記事では、いくつかの新機能をすばやく高レベルで見ていきます。新機能の完全なリストはhereで利用できます。

2. モジュラーシステム–ジグソープロジェクト

大きなものから始めましょう–Javaプラットフォームにモジュール性をもたらします。

モジュラーシステムは、OSGiフレームワークのシステムと同様の機能を提供します。 モジュールには依存関係の概念があり、パブリックAPIをエクスポートして、実装の詳細を非表示/プライベートに保つことができます。

ここでの主な動機の1つは、使用可能なメモリがはるかに少ないデバイスで実行できるモジュラーJVMを提供することです。 JVMは、アプリケーションに必要なモジュールとAPIのみで実行できます。 これらのモジュールの説明については、this linkを確認してください。

また、com.sun.*のようなJVM内部(実装)APIは、アプリケーションコードからアクセスできなくなりました。

簡単に言えば、モジュールは、Javaコード階層の最上位にあるmodule-info.javaというファイルに記述されます。

module com.example.java9.modules.car {
    requires com.example.java9.modules.engines;
    exports com.example.java9.modules.car.handling;
}

モジュールcarを実行するには、モジュールengineが必要であり、handlingのパッケージをエクスポートします。

より詳細な例については、OpenJDKProject Jigsaw: Module System Quick-Start Guideを確認してください。

3. 新しいHTTPクライアント

古いHttpURLConnectionの待望の交換。

新しいAPIは、java.net.httpパッケージの下にあります。

HTTP/2 protocolWebSocketの両方のハンドシェイクをサポートし、Apache HttpClientNettyJettyと同等のパフォーマンスを発揮する必要があります。

簡単なHTTPリクエストを作成して送信することにより、この新しい機能を見てみましょう。

更新:HTTP Client JEPはインキュベーターモジュールに移動されているため、パッケージjava.net.httpでは使用できなくなり、代わりにjdk.incubator.http.で使用できます。

3.1. クイックGETリクエスト

APIはBuilderパターンを使用しているため、すばやく簡単に使用できます。

HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI("https://postman-echo.com/get"))
  .GET()
  .build();

HttpResponse response = HttpClient.newHttpClient()
  .send(request, HttpResponse.BodyHandler.asString());

4. プロセスAPI

オペレーティングシステムプロセスを制御および管理するプロセスAPIが改善されました。

4.1. プロセス情報

クラスjava.lang.ProcessHandleには、ほとんどの新機能が含まれています。

ProcessHandle self = ProcessHandle.current();
long PID = self.getPid();
ProcessHandle.Info procInfo = self.info();

Optional args = procInfo.arguments();
Optional cmd =  procInfo.commandLine();
Optional startTime = procInfo.startInstant();
Optional cpuUsage = procInfo.totalCpuDuration();

currentメソッドは、現在実行中のJVMのプロセスを表すオブジェクトを返します。 Infoサブクラスは、プロセスに関する詳細を提供します。

4.2. プロセスの破壊

次に、destroy()を使用して実行中のすべての子プロセスを停止しましょう。

childProc = ProcessHandle.current().children();
childProc.forEach(procHandle -> {
    assertTrue("Could not kill process " + procHandle.getPid(), procHandle.destroy());
});

5. 小さな言語の変更

5.1. リソースを試す

Java 7では、try-with-resources構文では、ステートメントによって管理されているリソースごとに新しい変数を宣言する必要があります。

Java 9では、さらに改良が加えられています。リソースが最終変数または事実上最終変数によって参照される場合、try-with-resourcesステートメントは、新しい変数を宣言せずにリソースを管理できます。

MyAutoCloseable mac = new MyAutoCloseable();
try (mac) {
    // do some stuff with mac
}

try (new MyAutoCloseable() { }.finalWrapper.finalCloseable) {
   // do some stuff with finalCloseable
} catch (Exception ex) { }

5.2. ダイヤモンドオペレーターエクステンション

これで、匿名の内部クラスと組み合わせてダイヤモンド演算子を使用できます。

FooClass fc = new FooClass<>(1) { // anonymous inner class
};

FooClass fc0 = new FooClass<>(1) {
    // anonymous inner class
};

FooClass fc1 = new FooClass<>(1) { // anonymous inner class
};

5.3. インターフェイスプライベートメソッド

今後のJVMバージョンのインターフェースには、privateメソッドを含めることができます。これを使用して、長いデフォルトのメソッドを分割できます。

interface InterfaceWithPrivateMethods {

    private static String staticPrivate() {
        return "static private";
    }

    private String instancePrivate() {
        return "instance private";
    }

    default void check() {
        String result = staticPrivate();
        InterfaceWithPrivateMethods pvt = new InterfaceWithPrivateMethods() {
            // anonymous class
        };
        result = pvt.instancePrivate();
    }
}}

6. JShellコマンドラインツール

JShellはread-eval-printloop –略してREPLです。

簡単に言えば、Javaの宣言、ステートメント、式をAPIとともに評価するためのインタラクティブなツールです。 小さなコードスニペットをテストする場合に非常に便利です。そうしないと、mainメソッドを使用して新しいクラスを作成する必要があります。

jshell実行可能ファイル自体は、<JAVA_HOME>/binフォルダーにあります。

jdk-9\bin>jshell.exe
|  Welcome to JShell -- Version 9
|  For an introduction type: /help intro
jshell> "This is my long string. I want a part of it".substring(8,19);
$5 ==> "my long string"

対話型シェルには、履歴と自動補完が付属しています。また、ファイル、すべてまたは一部の記述されたステートメントへの保存やロードなどの機能も提供します。

jshell> /save c:\develop\JShell_hello_world.txt
jshell> /open c:\develop\JShell_hello_world.txt
Hello JShell!

コードスニペットは、ファイルのロード時に実行されます。

7. JCMDサブコマンド

jcmdコマンドラインユーティリティの新しいサブコマンドのいくつかを見てみましょう。 JVMにロードされているすべてのクラスとその継承構造のリストを取得します。

以下の例では、Eclipse Neonを実行しているJVMにロードされたjava.lang.Socketの階層を確認できます。

jdk-9\bin>jcmd 14056 VM.class_hierarchy -i -s java.net.Socket
14056:
java.lang.Object/null
|--java.net.Socket/null
|  implements java.io.Closeable/null (declared intf)
|  implements java.lang.AutoCloseable/null (inherited intf)
|  |--org.eclipse.ecf.internal.provider.filetransfer.httpclient4.CloseMonitoringSocket
|  |  implements java.lang.AutoCloseable/null (inherited intf)
|  |  implements java.io.Closeable/null (inherited intf)
|  |--javax.net.ssl.SSLSocket/null
|  |  implements java.lang.AutoCloseable/null (inherited intf)
|  |  implements java.io.Closeable/null (inherited intf)

jcmdコマンドの最初のパラメーターは、コマンドを実行するJVMのプロセスID(PID)です。

もう1つの興味深いサブコマンドはset_vmflagです。 JVMプロセスを再起動したり、起動パラメーターを変更したりすることなく、一部のJVMパラメーターをオンラインで変更できます。

サブコマンドjcmd 14056 VM.flags -allを使用して、使用可能なすべてのVMフラグを見つけることができます。

8. Мulti-解像度画像API

インターフェースjava.awt.image.MultiResolutionImageは、異なる解像度の画像のセットを1つのオブジェクトにカプセル化します。 特定のDPIメトリックと一連の画像変換に基づいて解像度固有の画像バリアントを取得するか、画像内のすべてのバリアントを取得できます。

java.awt.Graphicsクラスは、現在の表示DPIメトリックと適用された変換に基づいて、多重解像度画像からバリアントを取得します。

クラスjava.awt.image.BaseMultiResolutionImageは、基本的な実装を提供します。

BufferedImage[] resolutionVariants = ....
MultiResolutionImage bmrImage
  = new BaseMultiResolutionImage(baseIndex, resolutionVariants);
Image testRVImage = bmrImage.getResolutionVariant(16, 16);
assertSame("Images should be the same", testRVImage, resolutionVariants[3]);

9. 可変ハンドル

APIはjava.lang.invokeの下にあり、VarHandleMethodHandlesで構成されています。 これは、同様のパフォーマンスを持つオブジェクトフィールドおよび配列要素に対するjava.util.concurrent.atomicおよびsun.misc.Unsafe操作と同等のものを提供します。

Java 9 Modularシステムでは、アプリケーションコードからsun.misc.Unsafeにアクセスすることはできません。

10. パブリッシュ/サブスクライブフレームワーク

クラスjava.util.concurrent.Flowは、Reactive Streamsのパブリッシュ/サブスクライブフレームワークをサポートするインターフェイスを提供します。 これらのインターフェイスは、JVMで実行されている多数の非同期システム間の相互運用性をサポートします。

ユーティリティクラスSubmissionPublisherを使用して、カスタムコンポーネントを作成できます。

11. 統合JVMロギング

この機能により、JVMのすべてのコンポーネントに共通のロギングシステムが導入されます。 ロギングを行うためのインフラストラクチャを提供しますが、すべてのJVMコンポーネントからの実際のロギング呼び出しを追加しません。 また、JDKのJavaコードにロギングを追加しません。

ロギングフレームワークは、tagsのセットを定義します。たとえば、gccompilerthreadsなどです。 コマンドラインパラメータ-Xlogを使用して、起動時にログをオンにすることができます。

「debug」レベルを使用して「gc」タグでタグ付けされたメッセージを、装飾のない「gc.txt」というファイルに記録しましょう。

java -Xlog:gc=debug:file=gc.txt:none ...

-Xlog:helpは、可能なオプションと例を出力します。 ロギング構成は、jcmdコマンドを使用して実行時に変更できます。 GCログをinfoに設定し、ファイルにリダイレクトします– gc_logs:

jcmd 9615 VM.log output=gc_logs what=gc

12. 新しいAPI

12.1. 不変セット

java.util.Set.of() –指定された要素の不変のセットを作成します。 Java 8では、いくつかの要素のセットを作成するには、数行のコードが必要です。 これで、次のように簡単に実行できます。

Set strKeySet = Set.of("key1", "key2", "key3");

このメソッドによって返されるSetは、JVM内部クラスjava.util.ImmutableCollections.SetNであり、パブリックjava.util.AbstractSetを拡張します。 これは不変です。要素を追加または削除しようとすると、UnsupportedOperationExceptionがスローされます。

同じ方法で、配列全体をSetに変換することもできます。

12.2. ストリーミングするオプション

java.util.Optional.stream()は、オプションの要素でStreamsの機能を使用する簡単な方法を提供します。

List filteredList = listOfOptionals.stream()
  .flatMap(Optional::stream)
  .collect(Collectors.toList());

13. 結論

Java 9には、モジュラーJVMと、他の多くの新しい多様な改善と機能が付属します。

over on GitHubのソースコードを見つけることができます。