web-dev-qa-db-ja.com

静的対Javaの動的バインディング

現在、クラスの1つに割り当てを行っています。その中で、Java構文を使用して、staticおよびdynamic binding

静的バインディングはコンパイル時に発生し、動的バインディングは実行時に発生するという基本概念を理解していますが、実際に具体的にどのように機能するかはわかりません。

この例を示すオンラインの静的バインディングの例を見つけました。

public static void callEat(Animal animal) {
    System.out.println("Animal is eating");
}

public static void callEat(Dog dog) {
    System.out.println("Dog is eating");
}

public static void main(String args[])
{
    Animal a = new Dog();
    callEat(a);
}

callEatへの呼び出しは静的バインディングを使用するため、これは「animal is eating」を出力しますが、whyこれは静的バインディングと見なされます。

これまでのところ、私が見たソースのどれも、私が従うことができる方法でこれを説明することができませんでした。

73
user2309750

Javarevisitedブログ投稿 から:

静的バインディングと動的バインディングのいくつかの重要な違いを次に示します。

  1. Javaの静的バインディングはコンパイル時に発生し、動的バインディングは実行時に発生します。
  2. privatefinal、およびstaticのメソッドおよび変数は静的バインディングを使用し、コンパイラーによって結合されますが、仮想メソッドはランタイムオブジェクトに基づいてランタイム中に結合されます。
  3. 静的バインディングではType(Javaではclass)情報を使用してバインディングを行い、動的バインディングではオブジェクトを使用してバインディングを解決します。
  4. オーバーロードされたメソッドは静的バインディングを使用して結合され、オーバーライドされたメソッドは実行時に動的バインディングを使用して結合されます。

以下は、Javaの静的バインディングと動的バインディングの両方を理解するのに役立つ例です。

Javaの静的バインディングの例

public class StaticBindingTest {  
    public static void main(String args[]) {
        Collection c = new HashSet();
        StaticBindingTest et = new StaticBindingTest();
        et.sort(c);
    }
    //overloaded method takes Collection argument
    public Collection sort(Collection c) {
        System.out.println("Inside Collection sort method");
        return c;
    }
    //another overloaded method which takes HashSet argument which is sub class
    public Collection sort(HashSet hs) {
        System.out.println("Inside HashSet sort method");
        return hs;
    }
}

Output:内部コレクションのソート方法

Javaでの動的バインディングの例

public class DynamicBindingTest {   
    public static void main(String args[]) {
        Vehicle vehicle = new Car(); //here Type is vehicle but object will be Car
        vehicle.start(); //Car's start called because start() is overridden method
    }
}

class Vehicle {
    public void start() {
        System.out.println("Inside start method of Vehicle");
    }
}

class Car extends Vehicle {
    @Override
    public void start() {
        System.out.println("Inside start method of Car");
    }
}

出力:車の内部起動方法

92
Maulik Patel

メソッド呼び出しをメソッド本体に接続することをバインディングと呼びます。 Maulikが述べたように、「静的バインディングはバインディングにType(Javaのクラス)情報を使用し、動的バインディングはオブジェクトを使用してバインディングを解決します。」したがって、このコード:

public class Animal {
    void eat() {
        System.out.println("animal is eating...");
    }
}

class Dog extends Animal {

    public static void main(String args[]) {
        Animal a = new Dog();
        a.eat(); // prints >> dog is eating...
    }

    @Override
    void eat() {
        System.out.println("dog is eating...");
    }
}

結果は次のようになります。dog is eating ...使用するメソッドを見つけるためにオブジェクト参照を使用しているため。上記のコードをこれに変更すると:

class Animal {
    static void eat() {
        System.out.println("animal is eating...");
    }
}

class Dog extends Animal {

    public static void main(String args[]) {

        Animal a = new Dog();
        a.eat(); // prints >> animal is eating...

    }

    static void eat() {
        System.out.println("dog is eating...");
    }
}

animal is eating ...は静的メソッドであるため、Type(この場合はAnimal)を使用して、どの静的メソッドを呼び出すかを解決します。静的メソッドのほかに、プライベートメソッドと最終メソッドは同じアプローチを使用します。

15
Mr.Q

コンパイラーは、「a」のタイプがAnimalであることのみを知っています。これはコンパイル時に発生するため、静的バインディング(メソッドのオーバーロード)と呼ばれます。しかし、動的バインディングの場合は、Dogクラスメソッドを呼び出します。動的バインディングの例を次に示します。

public class DynamicBindingTest {

    public static void main(String args[]) {
        Animal a= new Dog(); //here Type is Animal but object will be Dog
        a.eat();       //Dog's eat called because eat() is overridden method
    }
}

class Animal {

    public void eat() {
        System.out.println("Inside eat method of Animal");
    }
}

class Dog extends Animal {

    @Override
    public void eat() {
        System.out.println("Inside eat method of Dog");
    }
}

出力:Dogの内部eatメソッド

3
user2378479

コンパイラーの設計中の静的バインディングと動的バインディングには3つの大きな違いがあり、variablesおよびproceduresruntime環境に転送される方法が異なります。これらの違いは次のとおりです。

静的バインディング:静的バインディングでは、次の3つの問題について説明します。

  • 手続きの定義

  • 名前の宣言(変数など)

  • 宣言の範囲

動的バインディング:動的バインディングで発生する3つの問題は次のとおりです。

  • プロシージャのアクティベーション

  • 名前のバインド

  • バインディングの寿命

2
hexpheus

親および子クラスの静的メソッドを使用:静的バインディング

public class test1 {   
    public static void main(String args[]) {
        parent pc = new child(); 
        pc.start(); 
    }
}

class parent {
    static public void start() {
        System.out.println("Inside start method of parent");
    }
}

class child extends parent {

    static public void start() {
        System.out.println("Inside start method of child");
    }
}

// Output => Inside start method of parent

動的バインディング:

public class test1 {   
    public static void main(String args[]) {
        parent pc = new child();
        pc.start(); 
    }
}

class parent {
   public void start() {
        System.out.println("Inside start method of parent");
    }
}

class child extends parent {

   public void start() {
        System.out.println("Inside start method of child");
    }
}

// Output => Inside start method of child
1
Soudipta Dutta

静的および動的バインディングが実際にどのように機能するかを理解するにはまたはコンパイラとJVMによってそれらがどのように識別されますか?

Mammalがメソッドspeak()およびHumanクラスを含む親クラスであり、Mammalクラスを拡張し、speak()メソッドをオーバーライドしてから、再びspeak(String language)でオーバーロードする例を以下に示します。

public class OverridingInternalExample {

    private static class Mammal {
        public void speak() { System.out.println("ohlllalalalalalaoaoaoa"); }
    }

    private static class Human extends Mammal {

        @Override
        public void speak() { System.out.println("Hello"); }

        // Valid overload of speak
        public void speak(String language) {
            if (language.equals("Hindi")) System.out.println("Namaste");
            else System.out.println("Hello");
        }

        @Override
        public String toString() { return "Human Class"; }

    }

    //  Code below contains the output and bytecode of the method calls
    public static void main(String[] args) {
        Mammal anyMammal = new Mammal();
        anyMammal.speak();  // Output - ohlllalalalalalaoaoaoa
        // 10: invokevirtual #4 // Method org/programming/mitra/exercises/OverridingInternalExample$Mammal.speak:()V

        Mammal humanMammal = new Human();
        humanMammal.speak(); // Output - Hello
        // 23: invokevirtual #4 // Method org/programming/mitra/exercises/OverridingInternalExample$Mammal.speak:()V

        Human human = new Human();
        human.speak(); // Output - Hello
        // 36: invokevirtual #7 // Method org/programming/mitra/exercises/OverridingInternalExample$Human.speak:()V

        human.speak("Hindi"); // Output - Namaste
        // 42: invokevirtual #9 // Method org/programming/mitra/exercises/OverridingInternalExample$Human.speak:(Ljava/lang/String;)V
    }
}

上記のコードをコンパイルし、javap -verbose OverridingInternalExampleを使用してバイトコードを調べてみると、コンパイラーが定数テーブルを生成し、すべてのメソッド呼び出しに整数コードを割り当て、プログラム自体に抽出して含めたプログラムのバイトコードを割り当てることがわかります(すべてのメソッド呼び出しの下のコメントを参照)

Program Bytecode

上記のコードを見ると、humanMammal.speak()human.speak()、およびhuman.speak("Hindi")のバイトコードが完全に異なっている(invokevirtual #4invokevirtual #7invokevirtual #9)ことがわかります。これは、コンパイラが引数リストとクラス参照に基づいて区別できるためです。このすべてがコンパイル時に静的に解決されるため、Method OverloadingStatic PolymorphismまたはStatic Binding

ただし、anyMammal.speak()humanMammal.speak()のバイトコードは同じです(invokevirtual #4)。コンパイラーによると、両方のメソッドがMammal参照で呼び出されるためです。

では、両方のメソッド呼び出しが同じバイトコードを持っている場合、JVMはどのメソッドを呼び出すべきかをどのように知るのでしょうか?

まあ、答えはバイトコード自体に隠されており、それはinvokevirtual命令セットです。 JVMはinvokevirtual命令を使用して、C++仮想メソッドに相当するJavaを呼び出します。 C++では、別のクラスで1つのメソッドをオーバーライドする場合、仮想メソッドとして宣言する必要がありますが、Javaでは、子クラスのすべてのメソッド(プライベートメソッド、最終メソッド、静的メソッドを除く)をオーバーライドできるため、デフォルトではすべてのメソッドが仮想です。

Javaでは、すべての参照変数は2つの隠されたポインターを保持します

  1. オブジェクトのメソッドとClassオブジェクトへのポインターを保持するテーブルへのポインター。例えば[speak()、speak(String)クラスオブジェクト]
  2. そのオブジェクトのデータ用にヒープに割り当てられたメモリへのポインタ。インスタンス変数の値。

したがって、すべてのオブジェクト参照は、そのオブジェクトのすべてのメソッド参照を保持するテーブルへの参照を間接的に保持します。 JavaはC++からこの概念を借用しており、このテーブルは仮想テーブル(vtable)として知られています。

Vtableは、仮想メソッド名と配列インデックスの参照を保持する構造体のような配列です。 JVMは、クラスをメモリにロードするときに、クラスごとに1つのvtableのみを作成します。

したがって、JVMはinvokevirtual命令セットに遭遇するたびに、そのクラスのvtableでメソッド参照をチェックし、この場合は参照ではなくオブジェクトからのメソッドである特定のメソッドを呼び出します。

これらはすべて実行時にのみ解決され、実行時にJVMがどのメソッドを呼び出すかを知るため、Method OverridingDynamic Polymorphismまたは単にPolymorphismまたはDynamic Binding

私の記事で詳細を読むことができます JVMはメソッドのオーバーロードと内部的なオーバーライドをどのように処理するか

1
Naresh Joshi