Obecnie wykonuję zadanie dla jednej z moich klas i muszę w nim podać przykłady, używając składni Javy, statycznego i dynamicznego wiązania .
Rozumiem podstawową koncepcję, że statyczne wiązanie ma miejsce w czasie kompilacji, a dynamiczne wiązanie ma miejsce w czasie wykonywania, ale nie mogę dowiedzieć się, jak właściwie działają.
Znalazłem przykład statycznego wiązania online, który daje ten przykład:
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);
}
I że to wypisze „zwierzę je”, ponieważ wywołanie callEat
używa statycznego wiązania , ale nie jestem pewien, dlaczego jest to uważane za statyczne wiązanie.
Jak dotąd żadnemu ze źródeł, które widziałem, nie udało się wyjaśnić tego w sposób, który mogę śledzić.
java
dynamic-binding
static-binding
user2309750
źródło
źródło
Odpowiedzi:
Z wpisu na blogu Javarevisited :
źródło
Łączenie wywołania metody z treścią metody jest nazywane Binding. Jak powiedział Maulik, „Wiązanie statyczne wykorzystuje informacje o typie (klasa w Javie) do wiązania, podczas gdy wiązanie dynamiczne wykorzystuje obiekt do rozwiązywania powiązań”. Więc ten kod:
Daje wynik: pies je ... ponieważ używa odniesienia do obiektu, aby znaleźć metodę do zastosowania. Jeśli zmienimy powyższy kod na ten:
Wytworzy: zwierzę je ... ponieważ jest to metoda statyczna, więc używa Type (w tym przypadku Animal) do rozstrzygnięcia, którą metodę statyczną należy wywołać. Oprócz metod statycznych metody prywatne i ostateczne wykorzystują to samo podejście.
źródło
a
faktycznie jest to plikDog
?Kompilator wie tylko, że typ „a” to
Animal
; dzieje się to w czasie kompilacji, przez co nazywa się to wiązaniem statycznym (przeciążenie metod). Ale jeśli jest to wiązanie dynamiczne, wywołaDog
metodę klasy. Oto przykład dynamicznego wiązania.Wyjście: Wewnątrz metoda jedzenia psa
źródło
Cóż, aby zrozumieć, jak statyczne i dynamiczne wiązanie faktycznie działa ? lub jak są identyfikowane przez kompilator i JVM?
Weźmy poniższy przykład, gdzie
Mammal
jest klasą nadrzędną, która ma metodęspeak()
iHuman
klasę rozszerzaMammal
, zastępujespeak()
metodę, a następnie ponownie ją przeciążaspeak(String language)
.Kiedy kompilujemy powyższy kod i próbujemy spojrzeć na kod bajtowy za pomocą
javap -verbose OverridingInternalExample
, widzimy, że kompilator generuje stałą tabelę, w której przypisuje kody całkowite do każdego wywołania metody i kodu bajtowego dla programu, który wyodrębniłem i włączyłem do samego programu ( zobacz komentarze poniżej każdego wywołania metody)Patrząc na powyższym kodzie widzimy, że bytecodes o
humanMammal.speak()
,human.speak()
ihuman.speak("Hindi")
są zupełnie różne (invokevirtual #4
,invokevirtual #7
,invokevirtual #9
) ponieważ kompilator jest w stanie rozróżniać między nimi na podstawie listy argumentów i odniesienie klasy. Ponieważ wszystko to jest rozwiązywane statycznie w czasie kompilacji, dlatego przeciążanie metod jest znane jako statyczny polimorfizm lub statyczne wiązanie .Ale kod bajtowy dla
anyMammal.speak()
ihumanMammal.speak()
jest taki sam (invokevirtual #4
), ponieważ zgodnie z kompilatorem obie metody są wywoływane wMammal
odwołaniu.Więc teraz pojawia się pytanie, czy oba wywołania metod mają ten sam kod bajtowy, to skąd JVM wie, którą metodę wywołać?
Cóż, odpowiedź jest ukryta w samym kodzie bajtowym i jest to
invokevirtual
zestaw instrukcji. JVM używainvokevirtual
instrukcji do wywołania Java odpowiednika metod wirtualnych C ++. W C ++, jeśli chcemy przesłonić jedną metodę w innej klasie, musimy zadeklarować ją jako wirtualną, ale w Javie wszystkie metody są domyślnie wirtualne, ponieważ możemy przesłonić każdą metodę w klasie potomnej (z wyjątkiem metod prywatnych, końcowych i statycznych).W Javie każda zmienna referencyjna zawiera dwa ukryte wskaźniki
Zatem wszystkie odwołania do obiektu pośrednio przechowują odwołanie do tabeli, która zawiera wszystkie odwołania do metod tego obiektu. Java zapożyczyła tę koncepcję z C ++ i ta tabela jest znana jako tabela wirtualna (vtable).
Tabela vtable jest strukturą przypominającą tablicę, która przechowuje nazwy metod wirtualnych i ich odniesienia w indeksach tablic. JVM tworzy tylko jedną tabelę vtable na klasę, kiedy ładuje klasę do pamięci.
Więc za każdym razem, gdy JVM napotyka
invokevirtual
zestaw instrukcji, sprawdza tabelę vtable tej klasy pod kątem odwołania do metody i wywołuje określoną metodę, która w naszym przypadku jest metodą z obiektu, a nie z odwołania.Ponieważ wszystko to jest rozwiązywane tylko w czasie wykonywania i podczas wykonywania, JVM dowiaduje się, którą metodę wywołać, dlatego przesłanianie metody jest znane jako dynamiczny polimorfizm lub po prostu polimorfizm lub dynamiczne wiązanie .
Możesz przeczytać więcej szczegółów w moim artykule Jak JVM obsługuje wewnętrzne przeciążanie i zastępowanie metod .
źródło
Istnieją trzy główne różnice między wiązaniem statycznym i dynamicznym podczas projektowania kompilatorów oraz sposobu przenoszenia zmiennych i procedur do środowiska uruchomieniowego środowiska . Te różnice są następujące:
Wiązanie statyczne : W wiązaniu statycznym omówiono trzy następujące problemy:
Definicja procedury
Deklaracja nazwy (zmienna itp.)
Zakres deklaracji
Dynamiczne wiązanie : trzy problemy, które występują w dynamicznym wiązaniu, są następujące:
Aktywacja procedury
Wiązanie nazwiska
Żywotność oprawy
źródło
Z metodą statyczną w klasie nadrzędnej i podrzędnej: Static Binding
Dynamiczne wiązanie:
źródło
Wszystkie odpowiedzi są poprawne, ale chcę dodać coś, czego brakuje. kiedy zastępujesz metodę statyczną, wygląda na to, że ją zastępujemy, ale w rzeczywistości nie jest to przesłanianie metody. Zamiast tego nazywa się to ukrywaniem metody. W Javie nie można przesłonić metod statycznych .
Spójrz na poniższy przykład:
W dynamicznym wiązaniu metoda jest wywoływana w zależności od typu odwołania, a nie od typu obiektu, który przechowuje zmienna referencyjna. Tutaj statyczne wiązanie ma miejsce, ponieważ ukrywanie metody nie jest dynamicznym polimorfizmem. Jeśli usuniesz statyczne słowo kluczowe przed eat () i sprawisz, że będzie to metoda niestatyczna, to pokaże ci dynamiczny polimorfizm, a nie ukrywanie metod.
Znalazłem poniższy link, aby wesprzeć moją odpowiedź: https://youtu.be/tNgZpn7AeP0
źródło
W przypadku statycznego wiązania typu obiektu określanego w czasie kompilacji, podczas gdy w dynamicznym wiązaniu typ obiektu jest określany w czasie wykonywania.
źródło
Ponieważ kompilator zna powiązanie w czasie kompilacji. Jeśli na przykład wywołasz metodę w interfejsie, kompilator nie będzie mógł tego wiedzieć, a powiązanie zostanie rozwiązane w czasie wykonywania, ponieważ rzeczywisty obiekt, na którym została wywołana metoda, może być jednym z kilku. Dlatego jest to środowisko wykonawcze lub dynamiczne wiązanie.
Twoje wywołanie jest powiązane z klasą Animal w czasie kompilacji, ponieważ określono typ. Gdybyś przekazał tę zmienną do innej metody w innym miejscu, nikt nie wiedziałby (poza tobą, ponieważ ją napisałeś), jaka to byłaby klasa. Jedyną wskazówką jest zadeklarowany typ zwierzęcia.
źródło