web-dev-qa-db-ja.com

invokeVirtualが存在するときにinvokeSpecialが必要な理由

Javaメソッドを呼び出すには3つのオペコードがあります。invokeStaticは静的メソッドの呼び出し専用であることは明らかです。

私が知る限り、invokespecialはコンストラクターとプライベートメソッドを呼び出すときに使用されます。では、実行時にプライベートメソッドとパブリックメソッドの呼び出しを区別する必要がありますか?同じopcodeでinvokevirtualを呼び出すことができますか?

JVMはプライベートメソッドとパブリックメソッドの定義を扱いますか?私の知る限り、パブリックキーワードとプライベートキーワードは、カプセル化の開発段階で必要なだけですか?

49
Ahmet Karakaya

http://www.artima.com/underthehood/invocationP.html 上記のリンクは、私の質問を追加する価値ある例を明確に示しています。

class Superclass {

    private void interestingMethod() {
        System.out.println("Superclass's interesting method.");
    }

    void exampleMethod() {
        interestingMethod();
    }
}

class Subclass extends Superclass {

    void interestingMethod() {
        System.out.println("Subclass's interesting method.");
    }

    public static void main(String args[]) {
        Subclass me = new Subclass();
        me.exampleMethod();
    }
}

上で定義されているようにサブクラスでmain()を呼び出すと、「スーパークラスの興味深いメソッド」を出力する必要があります。 invokevirtualを使用すると、「サブクラスの興味深いメソッド」が出力されます。どうして?それは、仮想マシンが、サブクラスであるオブジェクトの実際のクラスに基づいて、呼び出す興味深いMethod()を選択するためです。したがって、サブクラスのInterestingMethod()を使用します。一方、invokespecialを使用すると、仮想マシンは参照のタイプに基づいてメソッドを選択するため、スーパークラスのバージョンのInterestingMethod()が呼び出されます。

21
Ahmet Karakaya

から このサイト

Java VM Specを注意深く読むと、答えは簡単に見つかります。

Invokespecial命令とinvokevirtual命令の違いは、invokevirtualがオブジェクトのクラスに基づいてメソッドを呼び出すことです。 invokespecial命令は、インスタンスの初期化メソッド、プライベートメソッド、および現在のクラスのスーパークラスのメソッドを呼び出すために使用されます。

言い換えると、invokespecialは、メソッドの特定のクラスのバージョンを呼び出すために、動的バインディングに関係なくメソッドを呼び出すために使用されます。

32
Tim Pote

その説明をお読みいただきありがとうございます。メソッドの呼び出し中にアセンブリ命令の作成を特定するのに役立つ場合は、投票することを忘れないでください。ここでは、静的バインディングと動的バインディングについて説明します。

まず最初に、invokeStatic、invokeSpecial、invokeVirtual、invokeInterfaceなどが、コンパイルプロセス後にコンパイラによって生成されるアセンブリ命令であることをお伝えします。誰もが知っているように、コンパイル後に.classファイル形式を取得し、それを読み取ることができません。しかし、Javaという名前のツールを提供します"javap"

Javapコマンドを使用して、.classファイルのアセンブリー命令を読み取ることができます。デフォルトでは、プライベートメソッドアセンブリ命令を表示できないため、-privateを使用する必要があります。以下は、Javaコンパイラが生成したアセンブリ命令を表示するためのコマンドです。

  1. あなたがA.Javaクラスを持っているイメージング

    クラスA {public void printValue(){System.out.println( "Inside A"); }

    public static void callMethod(A a){a.printValue(); }}

  2. cmdプロンプトを開き、そのJavaファイルA.Javaを含むフォルダーに移動します。

  3. javac A.Javaを実行します。

  4. これで、アセンブリ命令を含むA.classファイルが生成されますが、読み取ることはできません。

  5. 次にjavap -c Aを実行します。

  6. メソッド呼び出しのアセンブリ生成を確認できます-> a.printValue();

  7. PrintValue()メソッドがプライベートの場合は、javap -c -private Aを使用する必要があります。

  8. PrintValue()をプライベート/スタティック/パブリック/プライベートスタティックの両方にすることができます。

  9. 最初にコンパイラが、メソッドが呼び出されるオブジェクトをチェックすることを覚えておいてください。次に、そのクラスタイプを検索し、利用可能かどうかにかかわらず、そのクラスでそのメソッドを見つけます。

注:ここで、呼び出しメソッドが静的の場合はinvokeStaticアセンブリが生成され、プライベートの場合はinvokeSpecialアセンブリ命令が生成され、パブリックの場合はinvokeVirtual命令が生成されることに注意してください。 publicメソッドは、invokeVirtual命令が生成されるたびに実行されることを意味しません。 super.printValue()の場合、Aのサブクラスからの呼び出しは例外です。つまり、AがBの親クラスであり、Bに同じメソッドprintValue()が含まれている場合は、invokeVirtual(dynamic)が生成されますが、BのprintValue()に最初のステートメントとしてsuper.printValue()がある場合、printValue( )のパブリックです。

これも試してみましょう:

class B extends A
{
public void printValue()
{
super.printValue();// invokeStatic
System.out.println("Inside B");
}

}

public class Test
{
public static void main(String[] arr)
{
    A a = new A();
    B b = new B();
    A.callMethod(a);// invokeVirtual
    A.callMethod(b);// invokeVirtual
}
}

-> Test.Javaで保存-> javac Test.Javaを実行-> javap -c -private Test

1
Maddy