Static Vs. Dynamische Bindung in Java


102

Ich mache gerade eine Aufgabe für eine meiner Klassen und muss darin Beispiele für die statische und dynamische Bindung unter Verwendung der Java-Syntax geben .

Ich verstehe das Grundkonzept, dass statische Bindung zur Kompilierungszeit und dynamische Bindung zur Laufzeit erfolgt, aber ich kann nicht herausfinden, wie sie tatsächlich spezifisch funktionieren.

Ich habe online ein Beispiel für statische Bindung gefunden, das dieses Beispiel enthält:

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);
}

Und dass dies "Tier frisst" drucken würde, weil der Aufruf zur callEatVerwendung der statischen Bindung lautet , aber ich bin mir nicht sicher, warum dies als statische Bindung angesehen wird.

Bisher hat es keine der Quellen, die ich gesehen habe, geschafft, dies auf eine Weise zu erklären, der ich folgen kann.



1
Beachten Sie, dass es verschiedene Konzepte gibt, die als "Bindung" bezeichnet werden. In diesem speziellen Fall entscheidet der Compiler (der "statische" Entscheidungen trifft, da sie zur Laufzeit nicht variieren) für diese Art der Bindung (bei der zwischen ähnlichen, aber nicht identischen Methoden "Signaturen" gewählt wird), dass der Parameter ein ist "Tier", da dies der (statische) Typ der Variablen "a" ist.
Hot Licks

(Es gibt Sprachen, in denen die Auswahl der spezifischen Methodensignatur bis zur Laufzeit verbleibt und callEat (Dog) ausgewählt wird.)
Hot Licks

Antworten:


113

Aus dem Javarevisited Blog Post :

Hier sind einige wichtige Unterschiede zwischen statischer und dynamischer Bindung:

  1. Die statische Bindung in Java erfolgt während der Kompilierungszeit, während die dynamische Bindung zur Laufzeit erfolgt.
  2. private, final Und staticMethoden und Variablen verwenden statische Bindung und durch Compiler gebondet werden , während virtuelle Methoden während der Laufzeit gebunden sind , basierend auf Laufzeitobjekt.
  3. Statische Bindung verwendet Type ( classin Java) Informationen zum Binden verwendet, während beim dynamischen Binden Objekte zum Auflösen der Bindung verwendet werden.
  4. Überladene Methoden werden mithilfe der statischen Bindung verbunden, während überschriebene Methoden zur Laufzeit mithilfe der dynamischen Bindung verbunden werden.

Hier ist ein Beispiel, das Ihnen hilft, sowohl die statische als auch die dynamische Bindung in Java zu verstehen.

Beispiel für statische Bindung in 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;
    }
}

Ausgabe : Sortiermethode Inside Collection

Beispiel für eine dynamische Bindung in 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");
    }
}

Ausgabe: Innenstartmethode des Autos



11
Ich verstehe den Unterschied immer noch nicht,
Technazi

9
Die statische Bindung von @technazi betrachtet nur den Typ (was auch immer vor dem Gleichen steht, z. B. Collection c = new HashSet (); es wird also nur als Collection-Objekt angesehen, wenn es sich tatsächlich um ein Hashset handelt). Die dyanmische Bindung berücksichtigt das tatsächliche Objekt (was nach dem Gleichen steht, damit es tatsächlich ein HashSet erkennt).
Mark

22

Das Verbinden eines Methodenaufrufs mit dem Methodenkörper wird als Bindung bezeichnet. Wie Maulik sagte "Die statische Bindung verwendet Typinformationen (Klasse in Java) für die Bindung, während die dynamische Bindung Object verwendet, um die Bindung aufzulösen." Also dieser Code:

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...");
    }
}

Wird das Ergebnis erzeugen: Hund frisst ... weil er die Objektreferenz verwendet, um herauszufinden, welche Methode verwendet werden soll. Wenn wir den obigen Code in diesen ändern:

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...");
    }
}

Es wird erzeugt: Tier isst ... weil es eine statische Methode ist, also verwendet es Type (in diesem Fall Animal), um zu entscheiden, welche statische Methode aufgerufen werden soll. Neben statischen Methoden verwenden private und endgültige Methoden denselben Ansatz.


1
Warum kann Java nicht ableiten, dass aes sich tatsächlich um eine DogKompilierungszeit handelt?
Minh Nghĩa

4

Der Compiler weiß nur, dass der Typ "a" ist Animal; Dies geschieht zur Kompilierungszeit, weshalb es als statische Bindung bezeichnet wird (Methodenüberladung). Wenn es sich jedoch um eine dynamische Bindung handelt, wird die DogKlassenmethode aufgerufen . Hier ist ein Beispiel für eine dynamische Bindung.

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");
    }
}

Ausgabe: Innenfressmethode des Hundes


Würde dies nicht einen Kompilierungsfehler wie "Kann nicht auf eine nicht statische Klasse / Methode aus einem statischen Kontext verweisen" auslösen? Ich bin immer verwirrt damit, wenn man bedenkt, dass main statisch ist. Danke im Voraus.
Amnor

3

Nun, um zu verstehen, wie statische und dynamische Bindung tatsächlich funktioniert? oder wie werden sie von Compiler und JVM identifiziert?

Nehmen wir das folgende Beispiel, in dem Mammaleine übergeordnete Klasse mit einer Methode speak()und einer erweiterten HumanKlasse Mammaldie speak()Methode überschreibt und sie dann erneut überlädt 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
    }
}

Wenn wir den obigen Code kompilieren und versuchen, den Bytecode mit zu betrachten javap -verbose OverridingInternalExample, können wir sehen, dass der Compiler eine konstante Tabelle generiert, in der er jedem Methodenaufruf und Bytecode für das Programm, das ich extrahiert und in das Programm selbst aufgenommen habe, Ganzzahlcodes zuweist ( siehe die Kommentare unter jedem Methodenaufruf)

Programm-Bytecode

Mit Blick auf obigem Code können wir , dass die Bytecodes sehen humanMammal.speak(), human.speak()und human.speak("Hindi")sind völlig anders ( invokevirtual #4, invokevirtual #7, invokevirtual #9) , da der Compiler zwischen ihnen zu unterscheiden , ist in der Lage , basierend auf der Liste der Argumente und Klassenreferenz. Da all dies zur Kompilierungszeit statisch gelöst wird, wird das Überladen von Methoden als statischer Polymorphismus oder statische Bindung bezeichnet .

Aber Bytecode für anyMammal.speak()und humanMammal.speak()ist gleich ( invokevirtual #4), da laut Compiler beide Methoden aufgerufen werdenMammal Referenz .

Nun stellt sich die Frage, ob beide Methodenaufrufe denselben Bytecode haben. Woher weiß JVM dann, welche Methode aufgerufen werden soll?

Nun, die Antwort ist im Bytecode selbst versteckt und es handelt sich um einen invokevirtualBefehlssatz. JVM verwendet dieinvokevirtual Anweisung, um ein Java-Äquivalent der virtuellen C ++ - Methoden aufzurufen. Wenn wir in C ++ eine Methode in einer anderen Klasse überschreiben möchten, müssen wir sie als virtuell deklarieren. In Java sind jedoch alle Methoden standardmäßig virtuell, da wir jede Methode in der untergeordneten Klasse überschreiben können (außer privaten, endgültigen und statischen Methoden).

In Java enthält jede Referenzvariable zwei versteckte Zeiger

  1. Ein Zeiger auf eine Tabelle, die wiederum Methoden des Objekts enthält, und ein Zeiger auf das Class-Objekt. zB [speak (), speak (String) Klassenobjekt]
  2. Ein Zeiger auf den auf dem Heap zugewiesenen Speicher für die Daten dieses Objekts, z. B. Werte von Instanzvariablen.

Alle Objektreferenzen enthalten also indirekt eine Referenz auf eine Tabelle, die alle Methodenreferenzen dieses Objekts enthält. Java hat dieses Konzept von C ++ ausgeliehen und diese Tabelle wird als virtuelle Tabelle (vtable) bezeichnet.

Eine vtable ist eine Array-ähnliche Struktur, die virtuelle Methodennamen und deren Referenzen auf Array-Indizes enthält. JVM erstellt nur eine vtable pro Klasse, wenn die Klasse in den Speicher geladen wird.

Wenn JVM also auf einen invokevirtualBefehlssatz stößt , überprüft es die vtable dieser Klasse auf die Methodenreferenz und ruft die spezifische Methode auf, die in unserem Fall die Methode eines Objekts ist, nicht die Referenz.

Da all dies nur zur Laufzeit gelöst wird und JVM zur Laufzeit erfährt, welche Methode aufgerufen werden muss, wird das Überschreiben von Methoden als dynamischer Polymorphismus oder einfach als Polymorphismus oder dynamische Bindung bezeichnet .

Sie können es mehr Details zu meinem Artikel lesen Wie funktioniert JVM Handle Methode Overloading und Übergeordnete Intern .


2

Es gibt drei Hauptunterschiede zwischen statischer und dynamischer Bindung beim Entwerfen der Compiler und wie Variablen und Prozeduren in die Laufzeitumgebung übertragen werden. Diese Unterschiede sind wie folgt:

Statische Bindung : Bei der statischen Bindung werden drei folgende Probleme diskutiert:

  • Definition eines Verfahrens

  • Angabe eines Namens (Variable usw.)

  • Geltungsbereich der Erklärung

Dynamische Bindung : Drei Probleme, die bei der dynamischen Bindung auftreten, sind folgende:

  • Aktivierung eines Verfahrens

  • Bindung eines Namens

  • Lebensdauer einer Bindung


1

Mit der statischen Methode in der übergeordneten und untergeordneten Klasse: Statische Bindung

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

Dynamische Bindung :

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

0

Alle Antworten hier sind richtig, aber ich möchte etwas hinzufügen, das fehlt. Wenn Sie eine statische Methode überschreiben, wird sie anscheinend überschrieben, aber tatsächlich wird keine Methode überschrieben. Stattdessen wird es als Methodenverstecken bezeichnet. Statische Methoden können in Java nicht überschrieben werden.

Schauen Sie sich das folgende Beispiel an:

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...");
    }
}

Bei der dynamischen Bindung wird die Methode abhängig von der Art der Referenz und nicht von der Art des Objekts aufgerufen , das die Referenzvariable enthält. Hier geschieht die statische Bindung, da das Ausblenden der Methode kein dynamischer Polymorphismus ist. Wenn Sie das statische Schlüsselwort vor eat () entfernen und es zu einer nicht statischen Methode machen, zeigt es Ihnen dynamischen Polymorphismus und nicht das Ausblenden von Methoden.

Ich habe den folgenden Link gefunden, um meine Antwort zu unterstützen: https://youtu.be/tNgZpn7AeP0


0

Im Fall des statischen Bindungstyps des Objekts, der zur Kompilierungszeit bestimmt wird, während im dynamischen Bindungstyp des Objekts zur Laufzeit bestimmt wird.



class Dainamic{

    void run2(){
        System.out.println("dainamic_binding");
    }

}


public class StaticDainamicBinding extends Dainamic {

    void run(){
        System.out.println("static_binding");
    }

    @Override
    void run2() {
        super.run2();
    }

    public static void main(String[] args) {
        StaticDainamicBinding st_vs_dai = new StaticDainamicBinding();
        st_vs_dai.run();
        st_vs_dai.run2();
    }

}

-3

Weil der Compiler die Bindung zur Kompilierungszeit kennt. Wenn Sie beispielsweise eine Methode auf einer Schnittstelle aufrufen, kann der Compiler dies nicht wissen und die Bindung wird zur Laufzeit aufgelöst, da das tatsächliche Objekt, auf dem eine Methode aufgerufen wird, möglicherweise eines von mehreren sein kann. Das ist also Laufzeit oder dynamische Bindung.

Ihr Aufruf ist zur Kompilierungszeit an die Animal-Klasse gebunden, da Sie den Typ angegeben haben. Wenn Sie diese Variable an eine andere Methode übergeben würden, würde niemand (außer Ihnen, weil Sie sie geschrieben haben) wissen, um welche tatsächliche Klasse es sich handelt. Der einzige Hinweis ist der deklarierte Tiertyp.


1
Nicht wahr. Der Compiler würde genau die gleiche Entscheidung treffen, wenn Sie eine Schnittstelle aufrufen würden.
Hot Licks

@HotLicks Genau die gleiche Entscheidung wie was? Wenn Sie eine Klasse kompilieren, um eine foo-Methode (String str) auf einer Schnittstelle aufzurufen, kann der Compiler zur Kompilierungszeit nicht wissen, für welche Klasse die foo-Methode (String str) aufgerufen werden soll. Nur zur Laufzeit kann der Methodenaufruf an eine bestimmte Klassenimplementierung gebunden werden.
Aaron

Eine statische Bindung an die spezifische Methodensignatur würde jedoch weiterhin auftreten. Der Compiler würde immer noch callEat (Animal) über callEat (Dog) auswählen.
Hot Licks

@ HotLicks Sicher, aber das ist nicht die Frage, die ich beantwortet habe. Vielleicht war es irreführend von mir: DI verglich es mit dem Aufrufen einer Schnittstelle, um hervorzuheben, dass der Compiler zum Zeitpunkt der Kompilierung nicht wissen kann, ob Sie tatsächlich eine andere Unterklasse / Implementierung instanziiert haben oder nicht.
Aaron

Tatsächlich kann der Compiler zur Kompilierungszeit (in diesem Fall) sehr leicht erkennen, dass "a" ein Hund ist. In der Tat muss es wahrscheinlich einige Anstrengungen unternehmen, um das zu "vergessen".
Hot Licks
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.