Statik Vs. Java'da Dinamik Bağlama


104

Şu anda sınıflarımdan biri için bir atama yapıyorum ve bunun içinde Java sözdizimi kullanarak statik ve dinamik bağlama örnekleri vermem gerekiyor .

Statik bağlamanın derleme zamanında gerçekleştiğini ve dinamik bağlamanın çalışma zamanında gerçekleştiğini temel kavramı anlıyorum, ancak özellikle nasıl çalıştıklarını anlayamıyorum.

Bu örneği veren bir statik bağlama çevrimiçi örneği buldum:

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

Ve bunun "hayvan yiyor" yazacağını çünkü çağrısı callEatstatik bağlama kullanıyor , ancak bunun neden statik bağlama olarak kabul edildiğinden emin değilim .

Şimdiye kadar gördüğüm kaynakların hiçbiri bunu takip edebileceğim bir şekilde açıklamayı başaramadı.



1
"Bağlama" olarak adlandırılan birkaç farklı kavram olduğunu unutmayın. Bu özel durumda, bu tür bir bağlama için (benzer ancak aynı olmayan yöntem "imzaları" arasında bir seçim yapılmasını içerir) derleyici (çalışma zamanında değişmedikleri için "statik" kararlar verir) parametrenin bir "Hayvan" çünkü bu, "a" değişkeninin (statik) türüdür.
Hot Licks

(Belirli yöntem imzasının seçiminin çalışma zamanına kadar bırakılacağı ve callEat (Dog) 'un seçileceği diller vardır.)
Hot Licks

Yanıtlar:


115

Gönderen Javarevisited blog post :

Statik ve dinamik bağlama arasındaki birkaç önemli fark şunlardır:

  1. Java'da statik bağlama, derleme sırasında oluşurken, çalışma zamanı sırasında dinamik bağlama gerçekleşir.
  2. private, finalVe staticyöntem ve değişkenler bağlama statik kullanımı ve sanal yöntemler çalışma zamanı nesneye dayalı çalışma sırasında bağlanmış ise derleyici tarafından bağlanır.
  3. Statik bağlama, bağlama için Type( classJava'da) bilgileri kullanırken dinamik bağlama, bağlamayı çözmek için nesneyi kullanır.
  4. Aşırı yüklenmiş yöntemler statik bağlama kullanılarak bağlanırken, geçersiz kılınan yöntemler çalışma zamanında dinamik bağlama kullanılarak bağlanır.

İşte Java'da hem statik hem de dinamik bağlamayı anlamanıza yardımcı olacak bir örnek.

Java'da Statik Bağlama Örneği

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

Çıktı : Koleksiyon İçi sıralama yöntemi

Java'da Dinamik Bağlama Örneği

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

Çıktı: Arabanın içten çalıştırma yöntemi



11
Hala farkı anlamıyorum,
technazi

9
@technazi statik bağlama sadece türe bakar (eşittir daha önce ne varsa, örneğin Collection c = new HashSet (); bu nedenle, aslında bir hashset olduğunda sadece bir koleksiyon nesnesi olarak görülecektir). Dinamik bağlama, gerçek nesneyi hesaba katar (eşitlerden sonra gelen, böylece aslında bir HashSet'i tanır).
Mark

22

Bir yöntem çağrısının yöntem gövdesine bağlanması Bağlama olarak bilinir. Maulik'in dediği gibi "Statik bağlama, bağlama için Tür (Java'da Sınıf) bilgilerini kullanırken Dinamik bağlama, bağlamayı çözmek için Nesne'yi kullanır." Yani bu kod:

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

Sonucu üretecektir: köpek yemek yiyor ... çünkü hangi yöntemi kullanacağını bulmak için nesne referansını kullanıyor. Yukarıdaki kodu şu şekilde değiştirirsek:

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

Üretecek: hayvan yiyor ... çünkü statik bir yöntem, bu nedenle hangi statik yöntemin çağrılacağını çözmek için Type (bu durumda Animal) kullanıyor. Statik yöntemlerin yanı sıra özel ve nihai yöntemler aynı yaklaşımı kullanır.


1
Java bunun aaslında Dogderleme zamanında olduğunu neden çıkaramıyor ?
Minh Nghĩa

4

Derleyici yalnızca "a" türünün Animal; bu, statik bağlama (Yöntem aşırı yükleme) olarak adlandırıldığı için derleme zamanında gerçekleşir. Ancak dinamik bağlama ise, Dogsınıf yöntemini çağırır . İşte dinamik bağlama örneği.

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

Çıktı: Köpeğin Inside eat yöntemi


Bu, "Statik olmayan bir sınıfa / yönteme statik bir bağlamdan başvurulamaz" gibi bir derleme hatası oluşturmaz mı? Ana'nın statik olduğunu aklımda tutarak hep kafam karıştı. Şimdiden teşekkürler.
Amnor

4

Peki statik ve dinamik bağlamanın gerçekte nasıl çalıştığını anlamak için ? veya derleyici ve JVM tarafından nasıl tanımlanırlar?

MammalBir metoda speak()ve Humansınıfa sahip bir ebeveyn sınıfının Mammalgenişlediği, speak()metodu geçersiz kıldığı ve sonra tekrar aşırı yüklediği aşağıdaki örneği ele alalım 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
    }
}

Yukarıdaki kodu derlediğimizde ve kullanarak bytecode'a bakmaya çalıştığımızda javap -verbose OverridingInternalExample, derleyicinin çıkardığım ve programın kendisine dahil ettiğim program için her yöntem çağrısına ve bayt koduna tamsayı kodları atadığı sabit bir tablo oluşturduğunu görebiliriz ( her yöntem çağrısının altındaki yorumlara bakın)

Bytecode Programlama

Yukarıdaki kod bakarak biz baytkodlarına olduğunu görebilirsiniz humanMammal.speak(), human.speak()ve human.speak("Hindi")tamamen farklı ( invokevirtual #4, invokevirtual #7, invokevirtual #9) derleyici argüman listesi ve sınıf referansı dayalı aralarında ayrım yapabiliyor çünkü. Tüm bunlar derleme zamanında statik olarak çözüldüğünden, Yöntem Aşırı Yüklemesinin Statik Polimorfizm veya Statik Bağlama olarak bilinmesinin nedeni budur .

Ancak bayt kodu için anyMammal.speak()ve humanMammal.speak()aynıdır ( invokevirtual #4) çünkü derleyiciye göre her iki yöntem de Mammalbaşvuru üzerine çağrılır .

Öyleyse şimdi soru, her iki yöntem çağrısının da aynı bayt koduna sahip olması durumunda ortaya çıkıyor, o zaman JVM hangi yöntemi çağıracağını nasıl biliyor?

Cevap bayt kodunun kendisinde gizlidir ve invokevirtualkomut setidir. JVM invokevirtual, C ++ sanal yöntemlerinin Java eşdeğerini çağırmak için talimatı kullanır . C ++ 'da, başka bir sınıftaki bir yöntemi geçersiz kılmak istiyorsak, onu sanal olarak bildirmemiz gerekir, ancak Java'da, tüm yöntemler varsayılan olarak sanaldır çünkü çocuk sınıfındaki her yöntemi geçersiz kılabiliriz (özel, son ve statik yöntemler hariç).

Java'da her referans değişkeni iki gizli işaret içerir

  1. Yine nesnenin yöntemlerini ve Class nesnesine bir işaretçi tutan bir tablo işaretçisi. örneğin [konuş (), konuş (String) Sınıf nesnesi]
  2. Bu nesnenin verileri için öbek üzerinde ayrılmış belleğe bir işaretçi, örneğin örnek değişkenlerinin değerleri.

Dolayısıyla, tüm nesne referansları, o nesnenin tüm yöntem referanslarını tutan bir tabloya dolaylı olarak bir referans içerir. Java bu kavramı C ++ 'dan ödünç almıştır ve bu tablo sanal tablo (vtable) olarak bilinir.

Bir vtable, sanal yöntem adlarını ve bunların referanslarını dizi indekslerinde tutan dizi benzeri bir yapıdır. JVM, sınıfı belleğe yüklediğinde sınıf başına yalnızca bir vtable oluşturur.

Dolayısıyla, JVM bir invokevirtualyönerge kümesiyle her karşılaştığında , o sınıfın vtable'ını yöntem başvurusu için kontrol eder ve bizim durumumuzda başvurudan değil bir nesneden gelen yöntem olan belirli yöntemi çağırır.

Tüm bunlar yalnızca çalışma zamanında ve çalışma zamanında çözüldüğünden, JVM hangi yöntemi çağıracağını bildiği için, Yöntemi Geçersiz Kılma Dinamik Polimorfizm veya kısaca Polimorfizm veya Dinamik Bağlama olarak bilinir .

JVM Metodu Aşırı Yüklemeyi ve Dahili Olarak Geçersiz Kılmayı Nasıl İşler? Makalemde daha fazla ayrıntı okuyabilirsiniz .


2

Derleyicileri tasarlarken statik ve dinamik bağlama arasında ve değişkenlerin ve prosedürlerin çalışma zamanı ortamına nasıl aktarılacağı arasında üç ana fark vardır . Bu farklılıklar aşağıdaki gibidir:

Statik Bağlama : Statik bağlamada aşağıdaki üç problem tartışılır:

  • Bir prosedürün tanımı

  • Bir adın beyanı (değişken vb.)

  • Beyannamenin kapsamı

Dinamik Bağlama : Dinamik bağlamada karşılaşılan üç sorun şunlardır:

  • Bir prosedürün etkinleştirilmesi

  • Bir adın bağlayıcılığı

  • Bağlamanın ömrü


1

Üst ve alt sınıftaki statik yöntemle: Statik Bağlama

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

Dinamik Bağlama:

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

Buradaki tüm cevaplar doğru ancak eksik olan bir şeyi eklemek istiyorum. Statik bir yöntemi geçersiz kıldığınızda, onu geçersiz kılıyoruz gibi görünür, ancak aslında bu yöntemi geçersiz kılmaz. Bunun yerine yöntem gizleme olarak adlandırılır. Statik yöntemler olamaz Java geçersiz kılınan olmak.

Aşağıdaki örneğe bakın:

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

Dinamik bağlamada yöntem, referans değişkeninin tuttuğu nesnenin türüne değil referans türüne bağlı olarak çağrılır Burada statik bağlama oluşur çünkü yöntem gizleme dinamik bir polimorfizm değildir. Eat () öğesinin önündeki statik anahtar kelimeyi kaldırırsanız ve onu statik olmayan bir yöntem haline getirirseniz, bu size yöntemi gizlemeyi değil, dinamik polimorfizmi gösterecektir.

Cevabımı desteklemek için aşağıdaki bağlantıyı buldum: https://youtu.be/tNgZpn7AeP0


0

Derleme zamanında belirlenen nesnenin statik bağlama türü durumunda, nesnenin dinamik bağlama türü çalışma zamanında belirlenir.



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

Çünkü derleyici bağlamayı derleme zamanında bilir. Örneğin, bir arabirimde bir yöntemi çağırırsanız, derleyici bilemez ve bağlama çalışma zamanında çözülür çünkü üzerinde çağrılan bir yönteme sahip gerçek nesne birkaç taneden biri olabilir. Bu nedenle bu çalışma zamanı veya dinamik bağlamadır.

Çağrınız, türü belirttiğiniz için derleme zamanında Animal sınıfına bağlıdır. Bu değişkeni başka bir yerde başka bir yönteme aktarırsanız, kimse onun gerçek sınıfın ne olacağını (sizden başka yazdığınız için) bilmez. Tek ipucu, beyan edilen Hayvan türü.


1
Doğru değil. Bir arayüzde arama yapıyor olsaydınız derleyici aynı kararı verirdi.
Hot Licks

@HotLicks Ne ile aynı karar? Bir arabirimde bir foo (String str) yöntemini çağırmak için bir sınıfı derlerseniz, derleyici derleme zamanında foo (String str) yönteminin hangi sınıfta çağrılması gerektiğini bilemez. Yalnızca çalışma zamanında yöntem çağrısı belirli bir sınıf uygulamasına bağlanabilir.
Aaron

Ancak belirli yöntem imzasına statik bağlama yine de gerçekleşir. Derleyici, callEat (Dog) yerine callEat (Animal) öğesini seçecektir.
Hot Licks

@HotLicks Tabii, ama cevapladığım soru bu değil. Belki de yanıltıcıydı: DI, derleme zamanında derleyicinin gerçekten farklı bir alt sınıf / uygulama başlatıp başlatmadığınızı bilemeyeceğini vurgulamak için onu bir arabirimde çağırmakla karşılaştırdı.
Aaron

Aslında, derleme zamanında derleyici (bu durumda) "a" nın bir Köpek olduğunu kolayca bilebilir. Aslında, bunu "unutmak" için büyük olasılıkla bazı yollara gitmesi gerekiyor.
Hot Licks
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.