「ベリファイアは、初期化される前に新しいオブジェクトを使用するコードを拒否します。」
バイトコード検証では、ベリファイアはリンク時に機能するため、メソッドのローカル変数のタイプが推測されます。メソッド引数のタイプは、クラスファイルのメソッドシグネチャにあるため、既知です。他のローカル変数のタイプは不明であり、推測されるため、上記のステートメントの「用途」はこれに関連していると思います。
編集:JVMのセクション4.9.4には次のように書かれています。
クラスmyClassのインスタンス初期化メソッド(§3.9)は、新しい未初期化オブジェクトをローカル変数0のthis引数と見なします。そのメソッドがmyClassの別のインスタンス初期化メソッドまたはこれに対する直接スーパークラスを呼び出す前に、メソッドが実行できる唯一の操作これは、myClass内で宣言されたフィールドを割り当てています。
上記のステートメントでのフィールドのこの割り当ては、オブジェクトのメモリが割り当てられたときの、インスタンス変数のデフォルトの初期値(intが0、floatが0.0fなど)への「初期」初期化です。仮想マシンがオブジェクトのインスタンス初期化メソッド(コンストラクター)を呼び出すとき、インスタンス変数のもう1つの「適切な」初期化があります。John Horstmannによって提供され
たリンクは、物事を明確にするのに役立ちました。したがって、これらのステートメントは当てはまりません。<init>「これは、メソッド内getfieldでそれを意味するものではなく、putfield別のメソッドが呼び出される前に許可さ<init>れます。」getfieldと_putfield命令は、クラス(またはクラスのインスタンス)のインスタンス変数(フィールド)にアクセス(および変更)するために使用されます。そして、これはインスタンス変数(フィールド)が初期化されたときにのみ発生する可能性があります。」
JVMSから:
各インスタンス初期化メソッド(§3.9)は、クラスObjectのコンストラクターから派生したインスタンス初期化メソッドを除き、インスタンスメンバーにアクセスする前に、この別のインスタンス初期化メソッドまたは直接スーパークラススーパーのインスタンス初期化メソッドを呼び出す必要があります。ただし、現在のクラスで宣言されているこのインスタンスフィールドは、インスタンス初期化メソッドを呼び出す前に割り当てることができます。
Java仮想マシンは、暗黙的または明示的にクラスの新しいインスタンスを作成するときに、最初にヒープにメモリを割り当てて、オブジェクトのインスタンス変数を保持します。メモリは、オブジェクトのクラスとそのすべてのスーパークラスで宣言されたすべての変数に割り当てられます。これには、非表示のインスタンス変数も含まれます。仮想マシンが新しいオブジェクト用のヒープメモリを確保するとすぐに、インスタンス変数をデフォルトの初期値に初期化します。仮想マシンが新しいオブジェクトにメモリを割り当て、インスタンス変数をデフォルト値に初期化すると、インスタンス変数に適切な初期値を与える準備が整います。Java仮想マシンは、clone()呼び出しのためにオブジェクトが作成されているかどうかに応じて、これを行うために2つの手法を使用します。clone()が原因でオブジェクトが作成されている場合、仮想マシンは、クローンが作成されているオブジェクトのインスタンス変数の値を新しいオブジェクトにコピーします。それ以外の場合、仮想マシンはオブジェクトのインスタンス初期化メソッドを呼び出します。インスタンス初期化メソッドは、オブジェクトのインスタンス変数を適切な初期値に初期化します。getfieldそして、この後のみ、とを使用できますputfield。
Javaコンパイラは、コンパイルするクラスごとに少なくとも1つのインスタンス初期化メソッド(コンストラクタ)を生成します。クラスがコンストラクターを明示的に宣言していない場合、コンパイラーは、スーパークラスの引数なしコンストラクターを呼び出すだけのデフォルトの引数なしコンストラクターを生成しました。そして当然のことながら、呼び出しの前にインスタンスフィールドで操作を行うと、コンパイルエラーが発生します
super()。
メソッドには、別のメソッドの呼び出し、インスタンス変数初期化子を実装するコード、コンストラクター本体のコードの3種類のコードを含めることができます。コンストラクターが同じクラス内の別のコンストラクターの明示的な呼び出し(呼び出し)で始まる場合、それに対応するthis()
<init><init>this()<init>メソッドは2つの部分で構成されます。
- 同じクラスの
<init>メソッドの呼び出し
- 対応するコンストラクターの本体を実装するバイトコード
コンストラクターが呼び出しで始まらずthis()、クラスがObjectでない場合、<init>メソッドには次の3つのコンポーネントがあります。
<init>スーパークラスメソッドの呼び出し
- インスタンス変数初期化子のバイトコード
- 対応するコンストラクターの本体を実装するバイトコード
コンストラクターが呼び出しで開始せずthis()、クラスがObjectである(そしてObjectにスーパークラスがない)場合、その<init>メソッドはスーパークラス<init>メソッドの呼び出しで開始できません。コンストラクターがスーパークラスコンストラクターの明示的な呼び出し(呼び出し)で始まる場合super()、その<init>メソッドは対応するスーパークラス<init>メソッドを呼び出します。
これはあなたの最初と2番目の質問に答えると思います。
更新しました:
例えば、
class Demo
{
int somint;
Demo() //first constructor
{
this(5);
//some other stuff..
}
Demo(int i) //second constructor
{
this.somint = i;
//some other stuff......
}
Demo(int i, int j) //third constructor
{
super();
//other stuffff......
}
}
コンパイラ(javac)からの上記の3つのコンストラクタのバイトコードは次のとおりです。
Demo();
Code:
Stack=2, Locals=1, Args_size=1
0: aload_0
1: iconst_5
2: invokespecial #1; //Method "<init>":(I)V
5: return
Demo(int);
Code:
Stack=2, Locals=2, Args_size=2
0: aload_0
1: invokespecial #2; //Method java/lang/Object."<init>":()V
4: aload_0
5: iload_1
6: putfield #3; //Field somint:I
9: return
Demo(int, int);
Code:
Stack=1, Locals=3, Args_size=3
0: aload_0
1: invokespecial #2; //Method java/lang/Object."<init>":()V
4: return
最初のコンストラクターでは、<init>メソッドは同じクラスのメソッドを呼び出すことから始まり<init>、対応するコンストラクターの本体を実行します。コンストラクターはで始まるため、this()対応する<init>メソッドには、インスタンス変数を初期化するためのバイトコードが含まれていません。
2番目のコンストラクターでは<init>、コンストラクターのメソッドは次のようになります。
- スーパークラス
<init>メソッド、つまりスーパークラスコンストラクター(argメソッドなし)の呼び出しではsuper()、最初のステートメントとして明示的なものが見つからなかったため、コンパイラーはデフォルトでこれを生成しました。
- インスタンス変数を初期化するためのバイトコード
someint。
- コンストラクター本体の残りの部分のバイトコード。