バイトコードの観点からJavaのボックス化とボックス化解除を分析します

1.開封と梱包の基本的な紹介

ボックス化とボックス化解除は、Javaで提供される2つの便利なシンタックスシュガーです。

ボクシングとは、基本的なデータ型をそのラッパー型に自動的に変換することです。intから整数への変換など。

ボックス化解除とは、ラッパータイプを対応する基本データタイプに変換することです。整数から整数への変換など。

次に例を示します。

Integer num1 = 1000;
int num2 = num1;

その中で、num1は整数型のオブジェクトであり、ここでの割り当て操作はボクシングプロセスです。

num2は基本型intの変数であり、num1を使用して値を割り当てることはボックス化解除プロセスです。

2.ボックス化解除とボクシングのバイトコード実装

バイトコードの観点から、ボックス化とボックス化解除がどのように実装されているかを見てみましょう。

では、Javaコードのバイトコードを取得する方法は?実際、javacを使用してJavaソースコードをコンパイルした後に取得するクラスファイルは、そのバイトコードファイルです。

ただし、このクラスファイルはバイナリ形式で存在するため、直接読み取ることはできません。したがって、クラスファイルを読み取り可能な形式に解析するのに役立つ別のコマンドjavapが必要です。

次のコードをtest.javaファイルに保存します(カテゴリ、メインメソッドなどはここでは省略されています)。

Integer num1 = 1000;
int num2 = num1;

javapコマンドの
使用次に、javacを使用してコンパイルし、次にjavap-v使用して逆コンパイルします。javapと-vパラメーターを組み合わせると、より包括的な情報を確認できます。

javac test.java
javap -v test

javapを使用して逆コンパイルした、次の結果を得ることができます。

         0: sipush        1000
         3: invokestatic  #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
         6: astore_1
         7: aload_1
         8: invokevirtual #3// Method java/lang/Integer.intValue:()I
        11: istore_2

ここでの逆コンパイルの結果には、6バイトコードの命令が含まれています。これらの命令を理解すると、一般的なボックス化解除とボックス化の実装の原則も理解できます。

これらの命令はスタックフレームのさまざまな操作に対応しているため、最初にスタックフレームの概念を確認します。

スタックフレームレビュー

誰もがJavaランタイムデータ領域のJava仮想マシンスタック精通していると思います

呼び出しから実行の完了までの各Javaメソッドのプロセスは、スタックフレームを仮想マシンスタックにプッシュしたり、仮想マシンスタックからプッシュしたりするプロセスに対応します。

メソッドの実行プロセスは、スタックフレームに対してさまざまな操作を実行することです。

ここでは、主にスタックフレーム内のオペランドスタックとローカル変数テーブルに焦点を当てます。
Javaスタックフレーム

命令実行分析

以下では、上記の6つの命令の実行プロセスを詳細に分析します。

Javaコード

Integer num1 = 1000;
int num2 = num1;

対応するバイトコード

         0: sipush        1000
         3: invokestatic  #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
         6: astore_1
         7: aload_1
         8: invokevirtual #3// Method java/lang/Integer.intValue:()I
        11: istore_2

最初の文:sipush 1000

Sipushはプッシュ操作であり、短い型の番号をスタックフレームに入れることを意味します。この文を実行した後のスタックフレームは次のとおりです。

sipush 1000
数値1000がオペランドスタックに配置されていることがわかります。

2番目の文:invokestatic#2

この文は静的メソッドを呼び出すためのものです。

では、どのメソッドが具体的に呼び出されますか?

背後に#2パラメータがあることがわかります。この#2は、定数プールのインデックスとして理解できます。

上記のコードに対応するバイトコードを出力することに加えて、javap-vコマンドを使用しました。もう1つの重要な部分は、定数プールです。

この逆コンパイルの定数プールを次の図に示し
ここに画像の説明を挿入
ます。インデックス#2の場所を確認でき、メソッド記述子が格納されています。実際、次のコメントを直接確認できます。このメソッドはInteger.valueOfメソッドです。

とりあえず誰もが知っているので、ここではコンスタントプールについては詳しく説明しません。

トピックに戻ります。命令invokestatic#2がInteger.valueOfメソッドを呼び出すことがわかったので、入力パラメーターは誰ですか?

最後の指示が何をしたか覚えていますか?

正しい。オペランドスタックの一番上に数値1000を置きます。スタックの最上位にある1000という数字は、実際にはInteger.valueOfの入力パラメーターです。

Integer.valueOfの戻り値はIntegerオブジェクトです。この命令を実行した後、1000がスタックからポップされ、結果のIntegerオブジェクトがスタックにプッシュされます。この整数オブジェクトは、Javaコードではnum1です。

このとき、スタックフレームは次のようになります
ここに画像の説明を挿入
。3番目の文:astore_1

この文は、オペランドスタックの最上位要素をポップし、ローカル変数テーブルの添え字が1である位置に配置することを意味します。

実行後の結果は次のとおりです。

ここに画像の説明を挿入

この文が実行されると、実行が終了した場合でも、Javaコードでは整数num1 = 1000になります。

ボクシングの結論

最初の3つのバイトコードはボクシング操作を完了します。上記の分析により、整数のボックス化はInteger.valueOfメソッドを呼び出すことによって実現されることがわかります。

他のデータ型でも同様です。

バイトコードの最後の3文は、int num2 = num1のプロセスに対応します。

4番目の文:aload_1
は、ローカル変数テーブルの位置が1である要素をオペランドスタックにプッシュすることを意味します。

この文の結果は次のとおりです。

aload_1
5番目の文:invokevirtual#3

invokestaticの2番目の文と同様に、この文もメソッド呼び出しの実行です。

これは、ここで呼び出されるメソッドの例にすぎません。

2番目の文の分析で与えられた定数プールの状況に参加します。ここで呼び出されるメソッドは、IntegerのintValueメソッドです。

オペランドは、前のステップでスタックにプッシュされた整数オブジェクトです。

実行後の結果は次のとおりです。

ここに画像の説明を挿入

6番目の文:istore_2

オペランドスタックの要素をローカル変数テーブルの2番目の位置に格納します。

ここに画像の説明を挿入
この時点で、実行は完了ですint num2 = num1

最後に、num1とnum2は、ローカル変数テーブルの1と2の位置に格納されます。

開封の結論

最後の3つのバイトコードの分析を通じて、整数型のボックス化解除はInteger.intValueを使用して実装されていることがわかります。

同じことが他のラッパータイプにも当てはまります。

3.この記事の要約

この記事では、ボックス化とボックス化解除の意味を紹介します。次に、javapコマンドを使用して、アセンブルとボックス化解除のバイトコード実装を取得しました。そして、これらのバイトコードを文ごとに分析します。

最後に、整数型の場合、ボックス化はInteger.valueOfメソッドを使用し、ボックス化解除はInteger.intValueメソッドを使用して結論を​​出します。


この記事がお役に立てば、私の元のWeChatパブリックアカウント「JavaTechnology Station」に注目して、私の記事をできるだけ早く受け取ることができます。
ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/vxzhg/article/details/103830715