【Java面接ポイント】Javaメソッド呼び出し——解析と割り当て

4189 ワード

メソッド呼び出しのバイトコード命令について:
invokestatic     invokespecial     invokevirtual       invokeInterface    invokedynamic
以上のいくつかの命令をよく知らない場合は、ここで他の人が書いた良い文を引用して、5つの方法でバイトコード命令を呼び出す違いを比較します.
JVM指令のinvokestatic,invokespecial,invokeinterface,invokevirtual,invokedynamic
invokespecialとinvokevirtualの違いを簡単に説明します.
1.invokespecialでは、インスタンス初期化メソッド、プライベートメソッド、親メソッドの3つのメソッドしか呼び出されません.コマンドは、特別な処理が必要なインスタンスメソッドを呼び出すために使用されます.
2.invokevirtualは、オブジェクトを呼び出すための命令のインスタンスメソッドであり、オブジェクトの実際のタイプに応じて割り当てられます.
 
次に私たちのパフォーマンスを始めます.
次の文を読む前に、まず一つの概念を明らかにします.Animal an=new Bird();このコードでは,Animalを静的タイプ,Birdをインスタンスタイプと呼ぶ.静的タイプはコンパイル期間でわかり,実際のタイプは実行期間で決定できる.
目次
Javaメソッド呼び出しは主に
1、解析呼び出し
2、静的アサイメントコール(静的単一アサイメントと静的多重アサイメント)
3、ダイナミックディスパッチコール(ダイナミックシングルディスパッチとダイナミックマルチディスパッチ)
 
Javaメソッド呼び出しは主に
1、解析呼び出し
メソッド呼び出しはメソッドの実行と等しくなく、メソッドの呼び出しフェーズの唯一のタスクは、呼び出されたメソッドのバージョンを決定することです.
 
解析:接続プロセスの最後のフェーズ
解析フェーズは、仮想マシンが定数プールのシンボル参照を直接参照に置き換えるプロセスです.
 
すべてのメソッド呼び出しのターゲットメソッドはClassファイル内の定数プール内のシンボル参照であり、クラスロードの解析フェーズでは、一部のシンボル参照が直接参照に変換されます.この解析は、プログラムが実際に実行される前に、メソッドが特定可能な呼び出しバージョンを持ち、実行期間が可変ではないことを前提としています.
コンパイル前に呼び出されたメソッドのバージョンを決定できるメソッド:
静的メソッド//invokestatic
コンストラクタメソッド//invokespecial
プライベートメソッド//invokespecial
親メソッド//invokespecial
以上の方法はinvokestaticとinvokespecial命令に対応する.解析呼び出しは静的プロセスであり,コンパイル中に決定できる.
 
2、静的アサイメントコール(静的単一アサイメントと静的多重アサイメント)
静的割り当ての最も古典的な例は、リロードであり、リロードは、実際のタイプではなくパラメータの静的タイプに基づいて判定の根拠となる.
静的タイプはコンパイル期間で知られるため,静的割り当てはコンパイル段階で発生する.(静的タイプと実際のタイプ、ディレクトリで説明されています)
コードを見てみましょう.
package cn.itcats.jvm.invokemethod;
/**
 *       
 * @author fatah
 *          
 */
public class Demo {

	static class Parent{
		
	}
	
	static class Child1 extends Parent {}
	static class Child2 extends Parent{}
	
	public void sayHello(Child1 child1) {
		System.out.println("child1 is saying");
	}
	
	public void sayHello(Child2 child2) {
		System.out.println("child2 is saying");
	}
	
	public void sayHello(Parent p) {
		System.out.println("Parent is saying");
	}
	
	public static void main(String[] args) {
		//         Parent       Child1
		Parent p1 = new Child1();
		Parent p2 = new Child2();
		Demo d = new Demo();
		
		//      ?
		d.sayHello(p1);
		d.sayHello(p2);
	}
}

実行結果:
Parent is saying Parent is saying
 
これが静的割り当ての例であり,前述を読んだ後,結果を解析するのは難しくないはずであり,静的タイプの決定はコンパイル中に決定された.
mainメソッドを変更
public static void main(String[] args) {
		//       ,          
		Parent p = new Child1();
		p = new Child2();
		
		//             ?      
		Demo d = new Demo();
		d.sayHello((Child2) p);   //   p    , Parent   p   Child2   p
	}

実行結果:
child 2 is sayingは静的タイプの変更により運転結果も変更されました
上記の例をまとめると、方法の静的割り当ては静的タイプによって決定され、静的タイプはコンパイル期間で決定されるため、静的割り当てはコンパイル期間でも決定される.
 
もう1つの例を見てみましょう.
package cn.itcats.jvm.invokemethod;

public class Demo2 {
	public void sayHello(byte i) {
		System.out.println("byte");
	}
	public void sayHello(short i) {
		System.out.println("short");
	}
	public void sayHello(int i) {
		System.out.println("int");
	}
	public void sayHello(int... i) {
		System.out.println("int...");
	}
	public void sayHello(long i) {
		System.out.println("long");
	}
	
	public static void main(String[] args) {
		Demo2 d = new Demo2();
		d.sayHello(2);  //            ,        ?
	}
}

実行結果:int
 
コンパイラは、呼び出されたメソッドのリロード・バージョンを決定できますが、リロード・バージョンが一意ではなく、より適切なバージョンしか決定できない場合が多いです.
主な原因は,文字量がパラメータとして伝達されるのは明示的な静的タイプがないためである.このフォントに最も近いタイプの方法しか選択できません.実際の仕事では避けなければならない.
 
3、ダイナミックディスパッチコール(ダイナミックシングルディスパッチとダイナミックマルチディスパッチ)
静的割当てとは異なり,動的割当ての最も古典的な例は書き換えであり,実行期間はメソッド受信者の実際のタイプに基づいてメソッドを選択する.
invokevirtualコマンドは、現在のクラスに書き換えられたメソッドが含まれているかどうかを優先的に探します.このクラスのメソッドを直接呼び出し、見つからない場合は、親クラスで探し、見つかるまで呼び出します.
具体的な手順:
  • オペランドスタックの一番目の要素が指すオブジェクトの実際のタイプ
  • を見つける.
  • 実際のタイプで定数の記述子と単純な名前が一致するメソッドが見つかった場合、アクセス権チェックを行い、通過した場合、このメソッドの直接参照を直接返し、検索プロセスが終了し、通過しない場合、例外を放出します.
  • 実際のタイプの各親を継承関係に従って下から上へ順次検索検証する
  • .
  • 常に見つからない場合、AbstractMethodError
  • を放出する