この記事では、java に関する関連知識を提供します。主に、Lambda ソース コードの表示方法に関する関連問題を紹介します。Lambda 式を使用すると、コードに対して多数の最適化を実行できます。複数の式を使用すると、次のことができます。たった 1 行のコードで多くのことができます。見てみましょう。皆さんのお役に立てれば幸いです。
![Java スキルのまとめ: Lambda ソース コードの読み方](https://img.php.cn/upload/article/000/000/067/625d3986b7349294.jpg)
推奨学習: 「java ビデオ チュートリアル 」
Java8 でラムダ式が追加されたことは誰もが知っています。ラムダ式を使用してください。コードを大幅に最適化でき、わずか数行のコードで多くのことを行うことができます。この章では Lambda を例として取り上げます。最初のセクションではその基礎となる実行原理について説明し、2 番目のセクションでは Lambda の一般的な姿勢について説明します仕事の流れ。
1. デモ
まず、以下に示すラムダ式のデモを見ていきます。コードは比較的単純で、新しいスレッドを開始して文章を出力するだけですが、図のコード () -> System.out.println (「ラムダが実行される」) については、多くの学生が混乱していると思います。 Java このコードを認識しますか?
記述方法を匿名内部クラスに変更すると、以下のように非常に明確になり、誰でも理解できるようになります。 ![](https://img.php.cn/upload/article/000/000/067/1a3a80f63ec24641de4cfc2dcf73d221-0.png)
ということですか。 () -> System.out.println (“ lambda が実行される”) この形式のコードは実際に内部クラスを作成しますか?実際、これは最も単純な Lambda 式です。IDEA ではソース コードとその基礎となる構造を確認できません。ここでは、その基礎となる実装を確認するいくつかの方法を紹介します。
2. 例外判定方法![](https://img.php.cn/upload/article/000/000/067/ac1d830706914d867f69dec6a8fa8233-1.png)
コード実行中に能動的に例外をスローし、スタックを出力することができます。スタックはその実行軌跡を説明します。一般に、この方法はシンプルで効率的であり、基本的には、以下に示すように、多くの場合に隠しコードを試してみましょう:
例外スタックから、JVM が現在のクラスの内部クラスを自動的に作成することがわかります。 class (エラースタックに複数回出現する $ は内部クラスを示します) 内部クラスのコードの実行中に例外がスローされますが、ここに示されているコードは不明なソースであるため、デバッグできません。 , 例外 どちらもコードの実行パスを公開でき、ブレークポイントを設定して再度実行することができますが、ラムダ式の場合は例外判定メソッドを通じて、内部クラスの存在が分かるだけで、内部クラスのソースコードを見ることはできません。クラス。
3. javap コマンドの使用方法![](https://img.php.cn/upload/article/000/000/067/d6e8bb264e16283788e85c537ac974c3-2.png)
javap は Java に付属している、クラスのバイトコードファイルを参照できるツールで、Java 基本環境がインストールされているコンピュータでは、以下のように javap コマンドを直接実行できます。 :
コマンドオプションのうち、バイトコードファイルの内容を完全に出力するには、主に -v -verbose コマンドを使用します。
次に、javap コマンドを使用して Lambda.class ファイルを表示します。説明中に、クラス ファイルに関する知識も取り入れます。 ![](https://img.php.cn/upload/article/000/000/067/0a9511363ee2bc92391eba58bfb7e045-3.png)
コマンド ウィンドウで Lambda.class の場所を見つけ、コマンド javap -verbose Lambda.class を実行すると、長いリストが表示されます。これらはアセンブリ命令と呼ばれます。実行してみましょう。次に説明しましょう (参考資料はすべて Java 仮想マシン仕様から引用されているため、1 つずつ引用することはしません):
アセンブリ手順では、定数プールで始まる型の長いリストを簡単に見つけることができます。定数プールと呼びます正式には英語では Run-Time Constant Pool と呼ばれます定数が詰め込まれたテーブルとして単純に理解していますこのテーブルにはコンパイル時の明確な数値とテキスト、クラス、メソッド、フィールドの型情報が含まれています、など。テーブル内の各要素は cp
info と呼ばれます。cp
info は一意の識別 (タグ) 名で構成されます。現在、合計のタグ タイプは次のとおりです:
投稿 これは私たちが解析した画像の一部です:
![Java スキルのまとめ: Lambda ソース コードの読み方](https://img.php.cn/upload/article/000/000/067/0a9511363ee2bc92391eba58bfb7e045-4.png)
画像内の「定数プール」という言葉は、現在の情報が定数プール; ![](https://img.php.cn/upload/article/000/000/067/030cb74b0ac71f5c2a1c2b586ff5f6b7-5.png)
- 各行は
cp_info
で、最初の列の #1 は定数プール内の 1 とマークされた位置を表します。
各行の 2 列目は、cp_info
の一意の識別子 (タグ) です。たとえば、Methodref は、上の表の CONSTANT_Methodref に対応します (上の表の値は、これは、現在の行がメソッドの名前、入力パラメータのタイプ、出力パラメータのタイプなどのメソッドの説明情報を表すことを意味します。具体的な意味は、Java 仮想マシンの仕様に記載されています。 Methodref のスクリーンショットは次のとおりです:
![Java スキルのまとめ: Lambda ソース コードの読み方](https://img.php.cn/upload/article/000/000/067/f3af61cff01566665259eaf320c355bf-6.png)
各行の 3 列目に、特定の値の場合、その値が直接表示されます。これは複素数値です。cp_info が表示されます。たとえば、図で赤でマークされた 2 の
の参照は、位置 13 と 14 にある 2 つの cp_info
を指します。13 は、を意味します。メソッド名が init であること、14 はメソッドに戻り値がないことを意味し、その組み合わせはメソッドの名前を表します。戻り値の型はパラメーターなしのコンストラクターです。
4 番目各行の列は特定の値です。
より重要な cp_info タイプについては、その意味を説明します:
- InvokeDynamic は、後で詳しく説明する動的呼び出しメソッドを表します。
#Fieldref は、フィールドの名前やタイプなどのフィールドの説明情報を表します。- NameAndType は、フィールドとメソッドのタイプの説明です。
- MethodHandle メソッド ハンドル、コンパイル中の動的呼び出しメソッドの集合名 その時点ではどのメソッドが特定であるかはわかりませんが、実行時にどのメソッドが呼び出されているかは確実にわかります。
- MethodType は動的メソッド タイプであり、そのメソッドのタイプが何であるかは、動的に実行するときにのみわかります。
-
上の図で赤でマークされた 3 つの場所 (MethodHandles とLambdaMetafactory java.lang.invoke パッケージの重要なメソッド。invoke パッケージは主に動的言語の機能を実装します。Java 言語が静的にコンパイルされる言語であることはわかっています。コンパイル中に、クラス、メソッド、フィールドなどのタイプは次のようになります。これは、クラス、メソッド、およびフィールドの型がコンパイル時には不明であり、実行時にのみ判明することを意味します。
たとえば、次のコード行: Runnable runnable = () -> System.out.println(“lambda is run”); コンパイラが () をコンパイルするとき、コンパイラはこの括弧が何であるかを認識しません。を実行するときにのみ、これが Runnable.run() メソッドを表していることがわかります。 invoke パッケージ内の多くのクラスは、これらの () を表すように設計されており、これをメソッド ハンドル (MethodHandler) と呼びます。コンパイル時、コンパイラはこれがメソッド ハンドルであることのみを認識し、どのメソッドが実際に実行されるかは知りません。それはそのときになってみないと分からないので、問題は、JVM が実行するときに、() メソッド ハンドルが実際に Runnable.run() メソッドを実行していることをどのようにして知ることができるのかということです。
まず、単純なメソッドのアセンブリ命令を見てみましょう。
![](https://img.php.cn/upload/article/000/000/067/936fe56253feafd14f20cc76e317fa34-7.png)
上の図から、() -> がわかります。シンプルなメソッドのシステム out.println(“lambda is run”) コード内の () は、実際には Runnable.run メソッドです。
上の図で赤 1 とマークされている #2 定数プールまで遡ります。InvokeDynamic は、これが動的呼び出しであることを示します。呼び出しは 2 つの定数プールの cp_info です。位置は # です。 0:#37。#37 は、// run:()Ljava/lang/Runnable を意味します。これは、JVM が実際に実行されるときに、Runnable.run() メソッドを動的に呼び出す必要があることを示します。アセンブリ命令を見ると () 実際には Runnable.run() であることがわかります。それを証明するために以下をデバッグしてみましょう。
上の図の 3 か所で LambdaMetafactory.metafactory という単語が見つかりました。公式ドキュメントを参照すると、このメソッドが実行中に実際のコードにリンクするための鍵であることがわかりました。以下に示すように、ブレークポイントを使用してデバッグします:
![Java スキルのまとめ: Lambda ソース コードの読み方](https://img.php.cn/upload/article/000/000/067/936fe56253feafd14f20cc76e317fa34-8.png)
メタファクトリ メソッドの入力パラメータ呼び出し元は、動的呼び出しが実際に発生する場所を表し、invokedName は呼び出しの名前を表します。メソッド、invokedType は呼び出しの複数の入力を表します。パラメータと出力パラメータ、samMethodType は特定の実装者のパラメータを表し、implMethod は実際の実装者を表し、instantiatedMethodType は implMethod と同等です。
上記を要約すると:
1: アセンブリ命令の単純なメソッドから、Runnable.run メソッドが実行されることがわかります;
2:実際の操作 JVM が単純なメソッドの invokedynamic 命令に遭遇すると、動的に LambdaMetafactory.metafactory メソッドを呼び出し、特定の Runnable.run メソッドを実行します。
したがって、ラムダ式値の特定の実行は、invokedynamic JVM 命令に起因すると考えられます。コンパイル時に何をすべきかはわかりませんが、まさにこの命令のおかげで、特定の実行を見つけることができます。コードを動的に実行する場合。
次に、アセンブリ命令の出力の最後を見てみますと、以下のように例外判定メソッドに内部クラスが存在します。上の図ではたくさんの矢印があり、現在の内部クラスのすべての情報がレイヤーごとに明確に表現されています。
4. 概要![Java スキルのまとめ: Lambda ソース コードの読み方](https://img.php.cn/upload/article/000/000/067/382bbb1b663693b45b1d440e83df998e-9.png)
要約すると、Lambda 式の実行は主に invokedynamic の JVM 命令に依存しています。デモするクラスのフル パスは、demo.eight.Lambda です。興味のある学生は次のことを行うことができます。自分たちで試してみてください。
早速、記事は終わりです。第 3 回シリーズをお楽しみに!
推奨学習: 「
Java ビデオ チュートリアル
」
以上がJava スキルのまとめ: Lambda ソース コードの読み方の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。