現在、クラスの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これは静的バインディングと見なされます。
これまでのところ、私が見たソースのどれも、私が従うことができる方法でこれを説明することができませんでした。
静的バインディングと動的バインディングのいくつかの重要な違いを次に示します。
- Javaの静的バインディングはコンパイル時に発生し、動的バインディングは実行時に発生します。
private
、final
、およびstatic
のメソッドおよび変数は静的バインディングを使用し、コンパイラーによって結合されますが、仮想メソッドはランタイムオブジェクトに基づいてランタイム中に結合されます。- 静的バインディングでは
Type
(Javaではclass
)情報を使用してバインディングを行い、動的バインディングではオブジェクトを使用してバインディングを解決します。- オーバーロードされたメソッドは静的バインディングを使用して結合され、オーバーライドされたメソッドは実行時に動的バインディングを使用して結合されます。
以下は、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"); } }
出力:車の内部起動方法
メソッド呼び出しをメソッド本体に接続することをバインディングと呼びます。 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)を使用して、どの静的メソッドを呼び出すかを解決します。静的メソッドのほかに、プライベートメソッドと最終メソッドは同じアプローチを使用します。
コンパイラーは、「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つの大きな違いがあり、variablesおよびproceduresがruntime環境に転送される方法が異なります。これらの違いは次のとおりです。
静的バインディング:静的バインディングでは、次の3つの問題について説明します。
手続きの定義
名前の宣言(変数など)
宣言の範囲
動的バインディング:動的バインディングで発生する3つの問題は次のとおりです。
プロシージャのアクティベーション
名前のバインド
バインディングの寿命
親および子クラスの静的メソッドを使用:静的バインディング
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
静的および動的バインディングが実際にどのように機能するかを理解するにはまたはコンパイラと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
を使用してバイトコードを調べてみると、コンパイラーが定数テーブルを生成し、すべてのメソッド呼び出しに整数コードを割り当て、プログラム自体に抽出して含めたプログラムのバイトコードを割り当てることがわかります(すべてのメソッド呼び出しの下のコメントを参照)
上記のコードを見ると、humanMammal.speak()
、human.speak()
、およびhuman.speak("Hindi")
のバイトコードが完全に異なっている(invokevirtual #4
、invokevirtual #7
、invokevirtual #9
)ことがわかります。これは、コンパイラが引数リストとクラス参照に基づいて区別できるためです。このすべてがコンパイル時に静的に解決されるため、Method OverloadingはStatic PolymorphismまたはStatic Binding。
ただし、anyMammal.speak()
とhumanMammal.speak()
のバイトコードは同じです(invokevirtual #4
)。コンパイラーによると、両方のメソッドがMammal
参照で呼び出されるためです。
では、両方のメソッド呼び出しが同じバイトコードを持っている場合、JVMはどのメソッドを呼び出すべきかをどのように知るのでしょうか?
まあ、答えはバイトコード自体に隠されており、それはinvokevirtual
命令セットです。 JVMはinvokevirtual
命令を使用して、C++仮想メソッドに相当するJavaを呼び出します。 C++では、別のクラスで1つのメソッドをオーバーライドする場合、仮想メソッドとして宣言する必要がありますが、Javaでは、子クラスのすべてのメソッド(プライベートメソッド、最終メソッド、静的メソッドを除く)をオーバーライドできるため、デフォルトではすべてのメソッドが仮想です。
Javaでは、すべての参照変数は2つの隠されたポインターを保持します
したがって、すべてのオブジェクト参照は、そのオブジェクトのすべてのメソッド参照を保持するテーブルへの参照を間接的に保持します。 JavaはC++からこの概念を借用しており、このテーブルは仮想テーブル(vtable)として知られています。
Vtableは、仮想メソッド名と配列インデックスの参照を保持する構造体のような配列です。 JVMは、クラスをメモリにロードするときに、クラスごとに1つのvtableのみを作成します。
したがって、JVMはinvokevirtual
命令セットに遭遇するたびに、そのクラスのvtableでメソッド参照をチェックし、この場合は参照ではなくオブジェクトからのメソッドである特定のメソッドを呼び出します。
これらはすべて実行時にのみ解決され、実行時にJVMがどのメソッドを呼び出すかを知るため、Method OverridingがDynamic Polymorphismまたは単にPolymorphismまたはDynamic Binding 。
私の記事で詳細を読むことができます JVMはメソッドのオーバーロードと内部的なオーバーライドをどのように処理するか 。