स्थैतिक बनाम। जावा में डायनामिक बाइंडिंग


103

मैं वर्तमान में अपनी कक्षाओं में से एक के लिए एक असाइनमेंट कर रहा हूं, और इसमें, मुझे उदाहरण देना है, स्थिर और गतिशील बंधन के जावा सिंटैक्स का उपयोग करना ।

मैं मूल अवधारणा को समझता हूं, कि स्थिर समय पर बाध्यकारी बाध्यकारी होता है और गतिशील बाइंडिंग रनटाइम पर होती है, लेकिन मैं यह पता नहीं लगा सकता कि वे वास्तव में विशेष रूप से कैसे काम करते हैं।

मुझे स्थैतिक बाइंडिंग ऑनलाइन का एक उदाहरण मिला जो यह उदाहरण देता है:

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स्थैतिक बंधन का उपयोग करता है , लेकिन मैं इस बात के लिए अनिश्चित हूं कि इसे स्थैतिक बंधन क्यों माना जाता है।

अब तक मैंने जिन स्रोतों को देखा है उनमें से कोई भी इस तरह से समझाने में कामयाब नहीं हुआ है कि मैं पालन कर सकता हूं।



1
ध्यान दें कि कई अलग-अलग अवधारणाएं हैं जिन्हें "बाध्यकारी" के रूप में संदर्भित किया जाता है। इस विशेष मामले में, इस प्रकार के बंधन के लिए (जिसमें समान लेकिन समान विधि "हस्ताक्षर" के बीच एक विकल्प शामिल है) संकलक (जो "स्थिर" निर्णय लेता है, क्योंकि वे रन समय में भिन्न नहीं होते हैं) यह तय करता है कि पैरामीटर एक है "पशु" क्योंकि वह (स्थिर) चर "a" का प्रकार है।
हॉट लिक्स

(ऐसी भाषाएं हैं, जहां रनटाइम तक विशिष्ट विधि हस्ताक्षर का विकल्प छोड़ दिया जाएगा, और कॉलएट (डॉग) का चयन किया जाएगा।)
हॉट लाइक्स

जवाबों:


114

से Javarevisited ब्लॉग पोस्ट :

स्थैतिक और गतिशील बंधन के बीच कुछ महत्वपूर्ण अंतर इस प्रकार हैं:

  1. जावा में स्टेटिक बाइंडिंग संकलन समय के दौरान होती है जबकि रनिंग के दौरान डायनामिक बाइंडिंग होती है।
  2. private, finalऔर staticविधियाँ और चर स्थिर बाइंडिंग का उपयोग करते हैं और संकलक द्वारा बंधित होते हैं जबकि रनटाइम ऑब्जेक्ट के आधार पर रनटाइम के दौरान वर्चुअल विधियों को बंधुआ किया जाता है।
  3. स्थैतिक बाध्यकारी उपयोग Type(class जावा में) जानकारी जबकि डायनेमिक बाइंडिंग बाइंडिंग को हल करने के लिए ऑब्जेक्ट का उपयोग करता है।
  4. ओवरलोड विधियों को स्थैतिक बंधन का उपयोग करके बंधुआ किया जाता है जबकि ओवरराइड विधियों को रनटाइम पर गतिशील बंधन का उपयोग करके बंधित किया जाता है।

यहां एक उदाहरण दिया गया है जो आपको जावा में स्थिर और गतिशील बंधन को समझने में मदद करेगा।

जावा में स्टेटिक बाइंडिंग उदाहरण

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

आउटपुट : इनसाइड कलेक्शन सॉर्ट विधि

जावा में डायनामिक बाइंडिंग का उदाहरण

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

आउटपुट: कार के अंदर की शुरुआत विधि



11
मुझे अभी भी अंतर समझ में नहीं आया,
Technazi

9
@technazi स्टैटिक बाइंडिंग केवल प्रकार को देखता है (जो कभी बराबरी से पहले होता है जैसे संग्रह c = new Hashet (), इसलिए इसे केवल एक संग्रह ऑब्जेक्ट के रूप में देखा जाएगा जब यह हैशसेट होता है)। डायनामिक बाइंडिंग वास्तविक वस्तु को ध्यान में रखता है (बराबरी के बाद क्या होता है इसलिए यह वास्तव में इसके हैशसेट को पहचानता है)।
मार्क

22

मेथड बॉडी के लिए मेथड कॉल को कनेक्ट करना बंधन के रूप में जाना जाता है। जैसा कि मौलिक ने कहा "स्टेटिक बाध्यकारी बाइंडिंग के लिए टाइप (जावा में क्लास) जानकारी का उपयोग करता है जबकि डायनामिक बाइंडिंग बाइंडिंग को हल करने के लिए ऑब्जेक्ट का उपयोग करता है।" तो यह कोड:

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

परिणाम देगा: कुत्ता खा रहा है ... क्योंकि यह वस्तु संदर्भ का उपयोग कर रहा है कि किस विधि का उपयोग करना है। यदि हम उपरोक्त कोड को इसमें बदलते हैं:

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

यह उत्पादन करेगा: जानवर खा रहा है ... क्योंकि यह एक स्थिर विधि है, इसलिए यह प्रकार (इस मामले में पशु) का उपयोग कर रहा है कि किस स्थिर विधि को कॉल करना है। स्थिर तरीकों के अलावा निजी और अंतिम तरीके समान दृष्टिकोण का उपयोग करते हैं।


1
क्यों जावा aवास्तव में एक Dogसंकलन समय पर कटौती नहीं कर सकता है?
मिन्ह नगूना

4

कंपाइलर केवल जानता है कि "ए" का प्रकार है 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");
    }
}

आउटपुट: डॉग के अंदर खाने की विधि


क्या यह एक संकलन त्रुटि नहीं करेगा, जैसे "स्थैतिक संदर्भ से गैर-स्थिर वर्ग / पद्धति का संदर्भ नहीं दे सकता"? मैं हमेशा इस बात से उलझन में रहता हूं कि मुख्य स्थिर है। अग्रिम में धन्यवाद।
अमनोर

3

अच्छी तरह से यह समझने के लिए कि स्थैतिक और गतिशील बंधन कैसे वास्तव में काम करता है? या कंपाइलर और जेवीएम द्वारा उनकी पहचान कैसे की जाती है?

आइए नीचे उदाहरण लेते हैं 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, तो हम देख सकते हैं कि कंपाइलर एक स्थिर तालिका बनाता है जहां यह प्रोग्राम के लिए हर विधि कॉल और बाइट कोड को पूर्णांक कोड प्रदान करता है जिसे मैंने निकाला है और कार्यक्रम में ही शामिल किया है ( हर विधि कॉल के नीचे टिप्पणी देखें)

कार्यक्रम बायटेकोड

ऊपर दिए गए कोड को देख कर हम इस बात का bytecodes देख सकते हैं humanMammal.speak(), human.speak()और human.speak("Hindi")पूरी तरह से अलग कर रहे हैं ( invokevirtual #4, invokevirtual #7, invokevirtual #9) क्योंकि संकलक तर्क सूची और वर्ग संदर्भ के आधार पर उन दोनों के बीच अंतर करने के लिए सक्षम है। क्योंकि यह सब संकलित समय पर संवैधानिक रूप से हल हो जाता है यही कारण है कि मेथड ओवरलोडिंग को स्टेटिक पॉलीमॉर्फिज़्म या स्टेटिक बाइंडिंग के रूप में जाना जाता है

लेकिन बायटेकोड के लिए anyMammal.speak()और humanMammal.speak()समान ( invokevirtual #4) है क्योंकि कंपाइलर के अनुसार दोनों तरीकों को कहा जाता हैMammal संदर्भ ।

तो अब सवाल आता है कि अगर दोनों मेथड कॉल में एक ही बाईटेकोड हो तो जेवीएम को किस तरीके से कॉल करना है?

खैर, इसका जवाब बाइटकोड में ही छिपा है और यह invokevirtualइंस्ट्रक्शन सेट है। JVM का उपयोग करता हैinvokevirtual जावा को C ++ वर्चुअल मेथड के बराबर करने के लिए निर्देश । C ++ में यदि हम किसी अन्य कक्षा में एक विधि को ओवरराइड करना चाहते हैं, तो हमें उसे आभासी घोषित करने की आवश्यकता है, लेकिन जावा में, सभी विधियां डिफ़ॉल्ट रूप से आभासी हैं, क्योंकि हम चाइल्ड क्लास (निजी, अंतिम और स्थिर तरीकों को छोड़कर) में हर विधि को ओवरराइड कर सकते हैं।

जावा में, प्रत्येक संदर्भ चर में दो छिपे हुए बिंदु होते हैं

  1. तालिका के लिए एक पॉइंटर जो फिर से ऑब्जेक्ट के तरीके और क्लास ऑब्जेक्ट के लिए एक पॉइंटर रखता है। जैसे [बोलो (), बोलो (स्ट्रिंग) कक्षा वस्तु]
  2. उस ऑब्जेक्ट के डेटा के लिए ढेर पर आवंटित मेमोरी के लिए एक पॉइंटर जैसे उदाहरण चर के मान।

तो सभी ऑब्जेक्ट रेफरेंस अप्रत्यक्ष रूप से एक टेबल के संदर्भ को रखते हैं, जो उस ऑब्जेक्ट के सभी विधि संदर्भों को रखता है। जावा ने इस अवधारणा को C ++ से उधार लिया है और इस तालिका को वर्चुअल टेबल (vtable) के रूप में जाना जाता है।

एक विएबेट संरचना की तरह एक सरणी है जो वर्चुअल तरीकों के नाम और सरणी सूचकांकों पर उनके संदर्भ रखती है। JVM प्रति वर्ग केवल एक व्यवहार्य बनाता है जब वह कक्षा को मेमोरी में लोड करता है।

इसलिए जब भी जेवीएम एक invokevirtualनिर्देश सेट के साथ मुठभेड़ करता है, तो वह विधि संदर्भ के लिए उस वर्ग की व्यवहार्यता की जांच करता है और उस विशिष्ट विधि को आमंत्रित करता है जो हमारे मामले में संदर्भ से नहीं एक वस्तु से विधि है।

क्योंकि यह सब केवल रनटाइम पर हल हो जाता है और रनटाइम में JVM को पता चल जाता है कि किस विधि को लागू करना है, इसीलिए मेथड ओवरराइडिंग को गतिशील बहुरूपता या बस बहुरूपता या गतिशील बंधन के रूप में जाना जाता है

आप इसे मेरे लेख पर अधिक विवरण कैसे पढ़ सकते हैं JVM हैंडल मेथड ओवरलोडिंग और आंतरिक रूप से ओवरराइडिंग


2

संकलक को डिजाइन करते समय स्थिर और गतिशील बंधन के बीच तीन प्रमुख अंतर हैं और रनवे के वातावरण में चर और प्रक्रियाओं को कैसे स्थानांतरित किया जाता है । ये अंतर इस प्रकार हैं:

स्टेटिक बाइंडिंग : स्थैतिक बाइंडिंग में निम्नलिखित तीन समस्याओं पर चर्चा की जाती है:

  • एक प्रक्रिया की परिभाषा

  • एक नाम की घोषणा (चर, आदि)

  • घोषणा का दायरा

डायनामिक बाइंडिंग : डायनेमिक बाइंडिंग में आने वाली तीन समस्याएं निम्नानुसार हैं:

  • एक प्रक्रिया का सक्रियण

  • एक नाम का बंधन

  • एक बंधन का जीवनकाल


1

माता-पिता और बच्चे के वर्ग में स्थिर विधि के साथ: स्टेटिक बाइंडिंग

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

0

यहां सभी उत्तर सही हैं, लेकिन मैं कुछ जोड़ना चाहता हूं जो गायब है। जब आप एक स्थैतिक विधि को ओवरराइड कर रहे होते हैं, तो ऐसा लगता है कि हम इसे ओवरराइड कर रहे हैं, लेकिन वास्तव में यह ओवरराइडिंग विधि नहीं है। इसके बजाय इसे मेथड हाइडिंग कहा जाता है। स्थैतिक तरीकेजावा में को ओवरराइड नहीं किया जा सकता है।

नीचे दिए गए उदाहरण देखें:

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

डायनामिक बाइंडिंग में, विधि को कहा जाता है संदर्भ के प्रकार के आधार पर न कि उस प्रकार के ऑब्जेक्ट को, जिसे रेफरेंस वैरिएबल यहां होल्ड कर रहा है, यहां स्टैटिक बाइंडिंगपेन्स हैं क्योंकि मेथड हाइडिंग डायनामिक पॉलीमोर्फिज्म नहीं है। यदि आप खाने के सामने स्थैतिक कीवर्ड हटाते हैं (और इसे एक गैर-स्थिर विधि बनाते हैं तो यह आपको गतिशील बहुरूपता दिखाएगा, न कि विधि-छिपाना।

मुझे अपने उत्तर का समर्थन करने के लिए नीचे दिया गया लिंक मिला: https://youtu.be/tNgZpn7AeP0


0

संकलित समय पर स्थिर बाध्यकारी प्रकार की वस्तु के मामले में जबकि गतिशील बंधन प्रकार के ऑब्जेक्ट को रनटाइम पर निर्धारित किया जाता है।



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

क्योंकि कंपाइलर बाध्यकारी समय पर बाध्यकारी जानता है। यदि आप उदाहरण के लिए, इंटरफ़ेस पर एक विधि लागू करते हैं, तो संकलक को पता नहीं चल सकता है और रनटाइम पर बाइंडिंग को हल किया जाता है, क्योंकि उस पर लागू की गई वास्तविक वस्तु कई में से एक हो सकती है। इसलिए वह रनटाइम या डायनेमिक बाइंडिंग है।

आपका आमंत्रण संकलन समय पर पशु वर्ग के लिए बाध्य है क्योंकि आपने प्रकार निर्दिष्ट किया है। यदि आप उस चर को किसी अन्य विधि से कहीं और पास करते हैं, तो किसी को भी पता नहीं चलेगा (इसके अलावा आप क्योंकि आपने इसे लिखा है) वह वास्तविक वर्ग क्या होगा। एकमात्र सुराग घोषित प्रकार का पशु है।


1
सच नहीं। यदि आप किसी इंटरफ़ेस पर कॉल कर रहे थे, तो कंपाइलर ठीक वही निर्णय लेगा।
हॉट लिक्स

@HotLicks उसी के समान निर्णय लें? यदि आप इंटरफ़ेस पर foo (String str) विधि को लागू करने के लिए एक वर्ग को संकलित करते हैं, तो संकलक को यह संकलन समय पर पता नहीं चल सकता है कि foo (String str) विधि को किस कक्षा में लागू किया जाना चाहिए। केवल रनटाइम में विधि आह्वान एक विशेष वर्ग कार्यान्वयन के लिए बाध्य हो सकता है।
एरोन

लेकिन विशिष्ट विधि हस्ताक्षर के लिए स्थिर बाध्यकारी अभी भी होता है। कंपाइलर अभी भी कॉलएट (डॉग) पर कॉलएट (पशु) को ले जाएगा।
हॉट लिप्स

@HotLicks ज़रूर, लेकिन यह वह सवाल नहीं है जिसका मैंने जवाब दिया। शायद यह मेरे लिए भ्रामक था: डीआई ने तुलना करने के लिए एक इंटरफ़ेस पर आह्वान करने के लिए कहा कि संकलन के समय संकलनकर्ता यह नहीं जान सकता है कि क्या आपने वास्तव में एक अलग उपवर्ग / कार्यान्वयन को तात्कालिक किया है या नहीं।
एरोन

दरअसल, कंपाइलर समय पर कंपाइलर (इस मामले में) बहुत आसानी से जान सकता है कि "a" एक डॉग है। वास्तव में, यह संभावना है कि "भूल" करने के लिए कुछ लंबाई तक जाना होगा।
हॉट लिक्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.