java जावा "पास-बाय-रेफरेंस" या "पास-बाय-वैल्यू" है?




methods parameter-passing (24)

एक लंबी कहानी कम करने के लिए, Java ऑब्जेक्ट्स में कुछ बहुत ही विशिष्ट गुण हैं।

सामान्य तौर पर, जावा आदिम प्रकार (है int, bool, char, double, आदि) कि सीधे मूल्य द्वारा पारित कर रहे हैं। तब जावा में वस्तुएं होती हैं (जो कुछ भी निकलती है java.lang.Object)। ऑब्जेक्ट्स वास्तव में हमेशा एक संदर्भ के माध्यम से संभाले जाते हैं (संदर्भ एक संकेतक है जिसे आप स्पर्श नहीं कर सकते हैं)। इसका मतलब है कि असल में, वस्तुओं को संदर्भ द्वारा पारित किया जाता है, क्योंकि संदर्भ आम तौर पर दिलचस्प नहीं होते हैं। हालांकि इसका मतलब यह है कि आप यह नहीं बदल सकते कि किस ऑब्जेक्ट को इंगित किया गया है क्योंकि संदर्भ स्वयं मूल्य से गुजरता है।

क्या यह अजीब और भ्रमित लगता है? आइए मान लें कि कैसे सी लागू संदर्भ द्वारा पास किया जाता है और मूल्य से गुजरता है। सी में, डिफ़ॉल्ट सम्मेलन मूल्य से गुजरता है। void foo(int x)मूल्य से एक int पास करता है। void foo(int *x)एक ऐसा कार्य है जो एक नहीं चाहता है int a, लेकिन एक int के लिए एक सूचक foo(&a):। कोई &एक परिवर्तनीय पता पास करने के लिए ऑपरेटर के साथ इसका उपयोग करेगा ।

इसे सी ++ पर ले जाएं, और हमारे पास संदर्भ हैं। सन्दर्भ मूल रूप से (इस संदर्भ में) सिंटैक्टिक चीनी है जो समीकरण के सूचक भाग को छुपाता है: void foo(int &x)जिसे foo(a)संकलक स्वयं जानता है कि यह एक संदर्भ है और गैर संदर्भ का पता aपारित किया जाना चाहिए। जावा में, ऑब्जेक्ट्स का जिक्र करने वाले सभी वेरिएबल वास्तव में संदर्भ प्रकार के होते हैं, असल में दागदार नियंत्रण (और जटिलता) के बिना अधिकांश इरादे और उद्देश्यों के संदर्भ में कॉल को मजबूर करने के लिए, उदाहरण के लिए, सी ++।

मैंने हमेशा सोचा कि जावा पास-बाय-रेफरेंस था

हालांकि, मैंने कुछ ब्लॉग पोस्ट देखे हैं (उदाहरण के लिए, यह ब्लॉग ) जो दावा करते हैं कि यह नहीं है।

मुझे नहीं लगता कि मैं उनके द्वारा किए गए भेद को समझता हूं।

स्पष्टीकरण क्या है?


जैसा कि कई लोगों ने पहले उल्लेख किया था, जावा हमेशा पास-दर-मूल्य होता है

यहां एक और उदाहरण है जो आपको अंतर को समझने में मदद करेगा ( क्लासिक स्वैप उदाहरण ):

public class Test {
  public static void main(String[] args) {
    Integer a = new Integer(2);
    Integer b = new Integer(3);
    System.out.println("Before: a = " + a + ", b = " + b);
    swap(a,b);
    System.out.println("After: a = " + a + ", b = " + b);
  }

  public static swap(Integer iA, Integer iB) {
    Integer tmp = iA;
    iA = iB;
    iB = tmp;
  }
}

प्रिंटों:

इससे पहले: ए = 2, बी = 3 के
बाद: ए = 2, बी = 3

ऐसा इसलिए होता है क्योंकि आईए और आईबी नए स्थानीय संदर्भ चर होते हैं जिनके पास पारित संदर्भों का समान मूल्य होता है (वे क्रमशः ए और बी को इंगित करते हैं)। इसलिए, आईए या आईबी के संदर्भों को बदलने की कोशिश केवल स्थानीय क्षेत्र में बदल जाएगी, न कि इस विधि के बाहर।


आप जावा में संदर्भ द्वारा कभी भी पास नहीं कर सकते हैं, और स्पष्ट रूप से एक तरीका यह है कि जब आप किसी विधि कॉल से एक से अधिक मान वापस करना चाहते हैं। सी ++ में कोड के निम्न बिट पर विचार करें:

void getValues(int& arg1, int& arg2) {
    arg1 = 1;
    arg2 = 2;
}
void caller() {
    int x;
    int y;
    getValues(x, y);
    cout << "Result: " << x << " " << y << endl;
}

कभी-कभी आप जावा में एक ही पैटर्न का उपयोग करना चाहते हैं, लेकिन आप नहीं कर सकते; कम से कम सीधे नहीं। इसके बजाय आप ऐसा कुछ कर सकते हैं:

void getValues(int[] arg1, int[] arg2) {
    arg1[0] = 1;
    arg2[0] = 2;
}
void caller() {
    int[] x = new int[1];
    int[] y = new int[1];
    getValues(x, y);
    System.out.println("Result: " + x[0] + " " + y[0]);
}

जैसा कि पिछले उत्तरों में बताया गया था, जावा में आप सरणी में एक सूचक के रूप में एक पॉइंटर पास कर रहे हैं getValues। यह पर्याप्त है, क्योंकि विधि सरणी तत्व को संशोधित करती है, और सम्मेलन द्वारा आप तत्व 0 को वापसी मूल्य रखने की अपेक्षा कर रहे हैं। जाहिर है आप इसे अन्य तरीकों से कर सकते हैं, जैसे कि आपके कोड को संरचित करना, इसलिए यह आवश्यक नहीं है, या उस वर्ग का निर्माण करना जिसमें वापसी मूल्य हो या इसे सेट करने की अनुमति हो। लेकिन उपरोक्त सी ++ में आपके लिए उपलब्ध सरल पैटर्न जावा में उपलब्ध नहीं है।


मैंने अभी देखा है कि आपने मेरे लेख का संदर्भ दिया है।

जावा स्पेक का कहना है कि जावा में सबकुछ पास-बाय-वैल्यू है। जावा में "पास-बाय-रेफरेंस" जैसी कोई चीज़ नहीं है।

इसे समझने की कुंजी यह है कि कुछ ऐसा है

Dog myDog;

एक कुत्ता नहीं है; यह वास्तव में एक कुत्ते के लिए एक सूचक है।

इसका मतलब क्या है, जब आपके पास है

Dog myDog = new Dog("Rover");
foo(myDog);

आप अनिवार्य रूप से बनाई गई Dog ऑब्जेक्ट का पता foo विधि पर पास कर रहे हैं।

(मैं अनिवार्य रूप से कहता हूं क्योंकि जावा पॉइंटर्स प्रत्यक्ष पते नहीं हैं, लेकिन इस तरह उनके बारे में सोचना सबसे आसान है)

मान लीजिए कि Dog ऑब्जेक्ट मेमोरी एड्रेस 42 पर रहता है। इसका मतलब है कि हम विधि को 42 पास करते हैं।

अगर विधि को परिभाषित किया गया था

public void foo(Dog someDog) {
    someDog.setName("Max");     // AAA
    someDog = new Dog("Fifi");  // BBB
    someDog.setName("Rowlf");   // CCC
}

चलो देखते हैं कि क्या हो रहा है।

  • पैरामीटर कुछ someDog मूल्य 42 पर सेट है
  • लाइन "एएए" पर
    • कुछ Dog को Dog लिए इंगित किया जाता है (पता 42 पर Dog वस्तु)
    • वह Dog (पता 42 पर एक) को अपना नाम मैक्स में बदलने के लिए कहा जाता है
  • लाइन पर "बीबीबी"
    • एक नया Dog बनाया गया है। मान लीजिए कि वह 74 पर है
    • हम 74 को पैरामीटर कुछ someDog असाइन करते हैं
  • लाइन "सीसीसी" पर
    • कुछ Dog को Dog लिए इंगित किया जाता है (पता 74 पर Dog वस्तु)
    • वह Dog (पता 74 पर वाला एक) को अपना नाम रोवल में बदलने के लिए कहा जाता है
  • फिर, हम वापस आते हैं

अब चलिए सोचें कि विधि के बाहर क्या होता है:

क्या myDog बदल गया?

कुंजी है

ध्यान में रखते हुए कि myDog Dog एक सूचक है , न कि एक वास्तविक Dog , जवाब नहीं है। myDog अभी भी मूल्य 42 है; यह अभी भी मूल Dog को इंगित कर रहा है (लेकिन ध्यान दें कि "एएए" रेखा के कारण, इसका नाम अब "मैक्स" है - अभी भी वही कुत्ता; myDog का मान नहीं बदला है।)

पते का पालन करना और इसके अंत में क्या बदलना पूरी तरह से मान्य है; हालांकि, परिवर्तनीय नहीं बदलता है।

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

सी ++ में, एडा, पास्कल और अन्य भाषाएं जो पास-बाय-रेफरेंस का समर्थन करती हैं, आप वास्तव में पारित चर को बदल सकते हैं।

यदि जावा पास-बाय-रेफरेंस myDog था, तो हमने ऊपर परिभाषित foo विधि बदल दी होगी जहां myDog इंगित कर रहा था जब उसने बीबीबी पर कुछ someDog असाइन किया था।

संदर्भ पैरामीटर के बारे में सोचें कि वेरिएबल के लिए उपनाम के रूप में उपनाम हैं। जब वह उपनाम असाइन किया जाता है, तो वैरिएबल जो पास किया गया था।


कुछ पदों में कुछ सुधार।

सी संदर्भ द्वारा पास का समर्थन नहीं करता है। यह हमेशा मूल्य से गुजरता है। सी ++ संदर्भ द्वारा पास का समर्थन करता है, लेकिन डिफ़ॉल्ट नहीं है और यह काफी खतरनाक है।

इससे कोई फर्क नहीं पड़ता कि जावा में मूल्य क्या है: वस्तु का आदिम या पता (मोटे तौर पर), यह हमेशा मूल्य से गुजरता है।

यदि जावा ऑब्जेक्ट "व्यवहार करता है" जैसे कि संदर्भ द्वारा पारित किया जा रहा है, यह उत्परिवर्तन की एक संपत्ति है और पासिंग तंत्र के साथ बिल्कुल कुछ नहीं है।

मुझे यकीन नहीं है कि यह इतना भ्रमित क्यों है, शायद इसलिए कि बहुत सारे जावा "प्रोग्रामर" औपचारिक रूप से प्रशिक्षित नहीं होते हैं, और इस प्रकार यह समझ में नहीं आता कि वास्तव में स्मृति में क्या चल रहा है?


जावा मूल्यों से वस्तुओं के संदर्भ पास करता है।


जावा मूल्य से संदर्भ पास करता है।

तो आप पारित संदर्भ को बदल नहीं सकते हैं।


असल में, ऑब्जेक्ट पैरामीटर को पुन: असाइन करना तर्क को प्रभावित नहीं करता है, उदाहरण के लिए,

private void foo(Object bar) {
    bar = null;
}

public static void main(String[] args) {
    String baz = "Hah!";
    foo(baz);
    System.out.println(baz);
}

"Hah!"इसके बजाय प्रिंट करेंगे null। इसका कारण यह है कि barयह मान की एक प्रति है baz, जो कि सिर्फ एक संदर्भ है"Hah!" ।यदि यह वास्तविक संदर्भ स्वयं था, तो fooफिर bazसे परिभाषित किया होगा null


जावा हमेशा मूल्य से गुजरता है, संदर्भ द्वारा पास नहीं होता है

सबसे पहले, हमें यह समझने की जरूरत है कि मूल्य से क्या पास है और संदर्भ द्वारा पास किया गया है।

मूल्य से गुजरने का मतलब है कि आप वास्तविक पैरामीटर के मान की स्मृति में प्रतिलिपि बना रहे हैं। यह वास्तविक पैरामीटर की सामग्री की एक प्रति है

संदर्भ द्वारा पास (जिसे पता द्वारा पास भी कहा जाता है) का अर्थ है कि वास्तविक पैरामीटर के पते की एक प्रति संग्रहित की जाती है

कभी-कभी जावा संदर्भ द्वारा पास का भ्रम दे सकता है। चलिए देखते हैं कि यह नीचे दिए गए उदाहरण का उपयोग करके कैसे काम करता है:

public class PassByValue {
    public static void main(String[] args) {
        Test t = new Test();
        t.name = "initialvalue";
        new PassByValue().changeValue(t);
        System.out.println(t.name);
    }

    public void changeValue(Test f) {
        f.name = "changevalue";
    }
}

class Test {
    String name;
}

इस कार्यक्रम का उत्पादन है:

changevalue

चलो कदम से कदम समझते हैं:

Test t = new Test();

जैसा कि हम सभी जानते हैं कि यह ढेर में एक वस्तु बनायेगा और संदर्भ मान को वापस टी पर वापस कर देगा। उदाहरण के लिए, मान लीजिए कि टी का मान है 0x100234(हम वास्तविक जेवीएम आंतरिक मूल्य नहीं जानते हैं, यह सिर्फ एक उदाहरण है)।

new PassByValue().changeValue(t);

फ़ंक्शन में संदर्भ टी पास करते समय यह ऑब्जेक्ट टेस्ट के वास्तविक संदर्भ मान को सीधे पास नहीं करेगा, लेकिन यह टी की प्रतिलिपि बनाएगा और फिर इसे फ़ंक्शन पर पास करेगा। चूंकि यह मूल्य से गुज़र रहा है , यह इसके वास्तविक संदर्भ के बजाय चर की एक प्रति पास करता है। चूंकि हमने कहा था कि टी का मूल्य था 0x100234, टी और एफ दोनों का एक ही मूल्य होगा और इसलिए वे एक ही वस्तु को इंगित करेंगे।

यदि आप संदर्भ f का उपयोग कर फ़ंक्शन में कुछ भी बदलते हैं तो यह ऑब्जेक्ट की मौजूदा सामग्री को संशोधित करेगा। यही कारण है कि हमें आउटपुट मिला changevalue, जिसे फ़ंक्शन में अपडेट किया गया है।

इसे और अधिक स्पष्ट रूप से समझने के लिए, निम्न उदाहरण पर विचार करें:

public class PassByValue {
    public static void main(String[] args) {
        Test t = new Test();
        t.name = "initialvalue";
        new PassByValue().changeRefence(t);
        System.out.println(t.name);
    }

    public void changeRefence(Test f) {
        f = null;
    }
}

class Test {
    String name;
}

क्या यह फेंक देगा NullPointerException? नहीं, क्योंकि यह केवल संदर्भ की एक प्रति पास करता है। संदर्भ से गुज़रने के मामले में, यह NullPointerExceptionनीचे देखा गया है, जैसा कि नीचे देखा गया था:

उम्मीद है कि यह मदद करेगा।


जावा मूल्य से एक कॉल है।

यह काम किस प्रकार करता है

  • आप हमेशा संदर्भ के मूल्य के बिट्स की एक प्रति पास करते हैं!

  • यदि यह एक आदिम डेटा प्रकार है, तो इन बिट्स में आदिम डेटा प्रकार का मान होता है, यही कारण है कि यदि हम विधि के अंदर हेडर के मान को बदलते हैं तो यह बाहर के परिवर्तनों को प्रतिबिंबित नहीं करता है।

  • यदि यह एक ऑब्जेक्ट डेटा प्रकार है जैसे Foo foo = new Foo () तो इस मामले में ऑब्जेक्ट के पते की प्रति फ़ाइल शॉर्टकट की तरह गुजरती है, मान लें कि हमारे पास C: \ डेस्कटॉप पर एक टेक्स्ट फ़ाइल abc.txt है और मान लीजिए कि हम शॉर्टकट बनाते हैं एक ही फ़ाइल और इसे C: \ desktop \ abc-shortcut के अंदर रखें ताकि जब आप C: \ desktop \ abc.txt से फ़ाइल तक पहुंचें और 'स्टैक ओवरफ़्लो' लिखें और फ़ाइल बंद करें और फिर आप शॉर्टकट से फ़ाइल खोलें तो आप लिखने के लिए प्रोग्रामर के लिए सबसे बड़ा ऑनलाइन समुदाय है ' तो कुल फ़ाइल परिवर्तन होगा ' स्टैक ओवरफ्लो प्रोग्रामर के लिए सबसे बड़ा ऑनलाइन समुदाय सीखने के लिए 'जिसका अर्थ यह है कि इससे कोई फर्क नहीं पड़ता कि आप फ़ाइल को खोलते हैं, हर बार जब हम एक ही फाइल तक पहुंच रहे थे, तो हम फू को एक फ़ाइल के रूप में मान सकते हैं और मान लें कि foo 123hd7h पर संग्रहीत है (मूल पता जैसे सी: \ डेस्कटॉप \ abc.txt ) पता और 234jdid (कॉपी किया गया पता जैसे सी: \ डेस्कटॉप \ abc-shortcut जिसमें वास्तव में फ़ाइल के मूल पते को शामिल किया गया है) .. तो बेहतर समझने के लिए शॉर्टकट फ़ाइल बनाएं और महसूस करें ...


मैं हमेशा इसे "कॉपी द्वारा पास" के रूप में सोचता हूं। यह मूल्य की एक प्रति प्राचीन या संदर्भ है। यदि यह एक आदिम है तो यह बिट्स की एक प्रति है जो मान हैं और यदि यह एक वस्तु है तो यह संदर्भ की एक प्रति है।

public class PassByCopy{
    public static void changeName(Dog d){
        d.name = "Fido";
    }
    public static void main(String[] args){
        Dog d = new Dog("Maxx");
        System.out.println("name= "+ d.name);
        changeName(d);
        System.out.println("name= "+ d.name);
    }
}
class Dog{
    public String name;
    public Dog(String s){
        this.name = s;
    }
}

जावा PassByCopy का उत्पादन:

नाम = मैक्सएक्स
नाम = फिडो

आदिम रैपर वर्ग और स्ट्रिंग्स अपरिवर्तनीय हैं इसलिए उन प्रकारों का उपयोग करने वाला कोई भी उदाहरण अन्य प्रकार / वस्तुओं के समान काम नहीं करेगा।


जावा हमेशा संदर्भ द्वारा मूल्य से तर्क पास करता है।

मुझे एक example माध्यम से इसे समझाएं:

public class Main{
     public static void main(String[] args){
          Foo f = new Foo("f");
          changeReference(f); // It won't change the reference!
          modifyReference(f); // It will modify the object that the reference variable "f" refers to!
     }
     public static void changeReference(Foo a){
          Foo b = new Foo("b");
          a = b;
     }
     public static void modifyReference(Foo c){
          c.setAttribute("c");
     }
}

मैं इसे चरणों में समझाऊंगा:

  1. f Foo प्रकार नामक एक संदर्भ घोषित करना और इसे "f" विशेषता वाले Foo के प्रकार की एक नई वस्तु को असाइन करना।

    Foo f = new Foo("f");
    

  2. विधि पक्ष से, किसी नाम के साथ प्रकार Foo का संदर्भ घोषित किया गया है और इसे प्रारंभ में null असाइन किया गया है।

    public static void changeReference(Foo a)
    

  3. जैसा कि आप विधि changeReference संदर्भ changeReference , संदर्भ a ऑब्जेक्ट को असाइन किया जाएगा जिसे तर्क के रूप में पारित किया गया है।

    changeReference(f);
    

  4. एक प्रकार का नाम Foo नामक संदर्भ घोषित कर रहा है और इसे "b" गुण के साथ Foo प्रकार की एक नई वस्तु को असाइन कर सकता है।

    Foo b = new Foo("b");
    

  5. a = b उस ऑब्जेक्ट को संदर्भित नहीं कर रहा है जिसकी विशेषता "b"

  6. जैसा कि आप modifyReference(Foo c) विधि कहते हैं, एक संदर्भ c बनाया गया है और ऑब्जेक्ट को "f" विशेषता के साथ असाइन किया गया है।

  7. c.setAttribute("c"); ऑब्जेक्ट की विशेषता को बदल देगा जो c बिंदुओं को संदर्भित करता है, और यह वही ऑब्जेक्ट है जो संदर्भित करता है।

मुझे आशा है कि अब आप समझेंगे कि कैसे जावा में तर्क काम करता है :)


प्रतिनिधित्व करते समय संदर्भ हमेशा एक मान होता है, इससे कोई फर्क नहीं पड़ता कि आप किस भाषा का उपयोग करते हैं।

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

स्मृति में डेटा में एक स्थान होता है और उस स्थान पर एक मान (बाइट, शब्द, जो भी हो) होता है। असेंबली में हमारे पास कुछ स्थान (उर्फ वैरिएबल) का नाम देने का एक सुविधाजनक समाधान है , लेकिन कोड को संकलित करते समय, असेंबलर नामित स्थान के साथ नाम को प्रतिस्थापित करता है जैसे कि आपका ब्राउज़र आईपी पते के साथ डोमेन नाम बदल देता है।

कोर के नीचे यह किसी भी भाषा में किसी भी भाषा का प्रतिनिधित्व किए बिना तकनीकी रूप से असंभव है (जब यह तुरंत मूल्य बन जाता है)।

आइए कहें कि हमारे पास एक वैरिएबल फू है, इसकी जगह 47 वें बाइट मेमोरी में है और इसका वैल्यू 5 है। हमारे पास एक और वैरिएबल रेफ 2 फ़ू है जो स्मृति में 223 वें बाइट पर है, और इसका मान 47 होगा। यह Ref2Foo तकनीकी चर हो सकता है , कार्यक्रम द्वारा स्पष्ट रूप से बनाया नहीं है। यदि आप किसी अन्य जानकारी के बिना 5 और 47 देख रहे हैं, तो आप केवल दो मान देखेंगे । यदि आप उन्हें संदर्भ के रूप में उपयोग करते हैं तो पहुंचने के लिए 5हमें यात्रा करना है:

(Name)[Location] -> [Value at the Location]
---------------------
(Ref2Foo)[223]  -> 47
(Foo)[47]       -> 5

इस तरह कूद-टेबल काम करते हैं।

अगर हम फू के मूल्य के साथ एक विधि / कार्य / प्रक्रिया को कॉल करना चाहते हैं, तो भाषा और इसके कई विधि आमंत्रण मोड के आधार पर, चर को विधि में पास करने के कुछ संभावित तरीके हैं:

  1. 5 को सीपीयू रजिस्टरों में से एक में कॉपी किया जाता है (यानी ईएक्स)।
  2. 5 ढेर के लिए PUSHD हो जाता है।
  3. 47 को सीपीयू रजिस्टरों में से एक में कॉपी किया जाता है
  4. 47 ढेर के लिए PUSHD।
  5. 223 को सीपीयू रजिस्टरों में से एक में कॉपी किया जाता है।
  6. 223 ढेर के लिए PUSHD हो जाता है।

किसी मान के ऊपर प्रत्येक मामले में - मौजूदा मान की एक प्रति - बनाई गई है, अब यह इसे संभालने के लिए प्राप्त करने की विधि तक है। जब आप विधि के अंदर "फू" लिखते हैं, तो इसे या तो ईएक्स से पढ़ा जाता है, या स्वचालित रूप से डिफ्रेंस किया जाता है , या डबल डिफ्रेंस किया जाता है, प्रक्रिया इस बात पर निर्भर करती है कि भाषा कैसे काम करती है और / या फू किस प्रकार का निर्देश देता है। यह डेवलपर से तब तक छिपा हुआ है जब तक कि वह dereferencing प्रक्रिया को circumvents। इसलिए संदर्भ एक मान है जब प्रतिनिधित्व किया जाता है, क्योंकि संदर्भ एक मान है जिसे संसाधित किया जाना चाहिए (भाषा स्तर पर)।

अब हमने विधि को फू पास कर दिया है:

  • यदि 1. और 2. यदि आप फू ( Foo = 9) बदलते हैं तो यह केवल स्थानीय दायरे को प्रभावित करता है क्योंकि आपके पास मूल्य की एक प्रति है। विधि के अंदर से हम यह भी निर्धारित नहीं कर सकते कि मूल फू कहाँ स्थित था।
  • यदि 3. और 4. मामले में आप डिफ़ॉल्ट भाषा संरचनाओं का उपयोग करते हैं और Foo ( Foo = 11) को बदलते हैं, तो यह वैश्विक रूप से Foo को बदल सकता है (भाषा पर निर्भर करता है, यानी जावा या पास्कल की procedure findMin(x, y, z: integer; var m : integer); )। हालांकि अगर भाषा आपको अव्यवस्था प्रक्रिया को बाधित करने की अनुमति देती है, तो आप 47कह सकते हैं 49। उस बिंदु पर फू को बदल दिया गया लगता है, क्योंकि आपने इसे स्थानीय पॉइंटर बदल दिया है। और यदि आप विधि ( Foo = 12) के अंदर इस फू को संशोधित करना चाहते हैं तो आप शायद प्रोग्राम (उर्फ सेगफॉल्ट) के निष्पादन को FUBAR करेंगे क्योंकि आप उम्मीद से अलग स्मृति को लिखेंगे, आप निष्पादन योग्य रखने के लिए निर्धारित क्षेत्र को भी संशोधित कर सकते हैं प्रोग्राम और इसके लिए लिखना चल रहा कोड संशोधित करेगा (फू अब नहीं है 47)। लेकिन फू का मूल्य47वैश्विक रूप से नहीं बदला, केवल विधि के अंदर एक, क्योंकि 47विधि के लिए एक प्रति भी था।
  • 5 और 6. मामले में यदि आप 223विधि के अंदर संशोधित करते हैं तो यह 3 या 4 के समान मेहेम बनाता है। (एक पॉइंटर, जो अब खराब मान को इंगित करता है, जिसे फिर से पॉइंटर के रूप में उपयोग किया जाता है) लेकिन यह अभी भी एक स्थानीय है समस्या, 223 की प्रतिलिपि बनाई गई थी । हालांकि यदि आप अव्यवस्था Ref2Foo(यानी 223) तक पहुंचने में सक्षम हैं , तो इंगित मूल्य तक पहुंचने और संशोधित करने के लिए 47, कहें 49, यह वैश्विक स्तर पर फू को प्रभावित करेगा , क्योंकि इस मामले में तरीकों की प्रतिलिपि मिली है 223लेकिन संदर्भ 47केवल एक बार मौजूद है, और इसे बदलना करने के लिए 49हर नेतृत्व करेंगे Ref2Fooएक गलत मान के लिए डबल-dereferencing।

महत्वहीन विवरणों पर नाइटपिकिंग, यहां तक ​​कि भाषाएं जो पास-दर-संदर्भ करती हैं, वे कार्यों को मूल्यों को पास कर देंगे, लेकिन उन कार्यों को पता है कि उन्हें इसे संदर्भित उद्देश्यों के लिए उपयोग करना है। यह पास-द-रेफरेंस-ए-वैल्यू प्रोग्रामर से सिर्फ छिपा हुआ है क्योंकि यह व्यावहारिक रूप से बेकार है और शब्दावली केवल पास-बाय-रेफरेंस है

सख्त पास-बाय-वैल्यू भी बेकार है, इसका मतलब यह होगा कि जब भी हम सरणी के साथ एक विधि को तर्क के रूप में कॉल करते हैं तो एक 100 एमबीटी सरणी को प्रतिलिपि बनाना चाहिए, इसलिए जावा कठोर रूप से पास-दर-मूल्य नहीं हो सकता है। प्रत्येक भाषा इस विशाल सरणी (एक मान के रूप में) का संदर्भ देगी और या तो कॉपी-ऑन-राइट तंत्र को नियोजित करेगी यदि उस सरणी को विधि के अंदर स्थानीय रूप से बदला जा सकता है या विधि (जावा के रूप में) को वैश्विक स्तर पर सरणी को संशोधित करने की अनुमति देता है (से कॉलर का दृश्य) और कुछ भाषाएं संदर्भ के मूल्य को संशोधित करने की अनुमति देती हैं।

तो संक्षेप में और जावा की अपनी शब्दावली में, जावा पास-बाय-वैल्यू है जहां मूल्य हो सकता है: या तो वास्तविक मूल्य या एक मान जो संदर्भ का प्रतिनिधित्व करता है ।


जावा में सब कुछ संदर्भ है, इसलिए जब आपके पास कुछ ऐसा है: Point pnt1 = new Point(0,0);जावा निम्न करता है:

  1. नया प्वाइंट ऑब्जेक्ट बनाता है
  2. नया प्वाइंट संदर्भ बनाता है और उस बिंदु को संदर्भित करता है (संदर्भित) पहले बनाए गए प्वाइंट ऑब्जेक्ट पर।
  3. यहां से, प्वाइंट ऑब्जेक्ट लाइफ के माध्यम से, आप उस ऑब्जेक्ट को pnt1 संदर्भ के माध्यम से एक्सेस करेंगे। तो हम कह सकते हैं कि जावा में आप इसके संदर्भ के माध्यम से ऑब्जेक्ट में हेरफेर करते हैं।

जावा संदर्भ द्वारा विधि तर्क पास नहीं करता है; यह उन्हें मूल्य से गुजरता है। मैं इस साइट से उदाहरण का उपयोग करूंगा :

public static void tricky(Point arg1, Point arg2) {
  arg1.x = 100;
  arg1.y = 100;
  Point temp = arg1;
  arg1 = arg2;
  arg2 = temp;
}
public static void main(String [] args) {
  Point pnt1 = new Point(0,0);
  Point pnt2 = new Point(0,0);
  System.out.println("X1: " + pnt1.x + " Y1: " +pnt1.y); 
  System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y);
  System.out.println(" ");
  tricky(pnt1,pnt2);
  System.out.println("X1: " + pnt1.x + " Y1:" + pnt1.y); 
  System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y);  
}

कार्यक्रम का प्रवाह:

Point pnt1 = new Point(0,0);
Point pnt2 = new Point(0,0);

दो अलग-अलग बिंदु ऑब्जेक्ट को दो अलग-अलग संदर्भों से जोड़ना।

System.out.println("X1: " + pnt1.x + " Y1: " +pnt1.y); 
System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y);
System.out.println(" ");

जैसा कि अपेक्षित आउटपुट होगा:

X1: 0     Y1: 0
X2: 0     Y2: 0

इस लाइन पर 'पास-बाय-वैल्यू' नाटक में चला जाता है ...

tricky(pnt1,pnt2);           public void tricky(Point arg1, Point arg2);

संदर्भ pnt1और pnt2कर रहे हैं मूल्य द्वारा पारित मुश्किल विधि के लिए, जिसका अर्थ है कि अब तुम्हारा संदर्भ pnt1और pnt2उनकी राशि copiesनामित arg1और arg2तो pnt1और arg1 अंक एक ही वस्तु है। ( pnt2और के लिए वही arg2)

में trickyविधि:

 arg1.x = 100;
 arg1.y = 100;

trickyविधि में अगला

Point temp = arg1;
arg1 = arg2;
arg2 = temp;

यहां, आप पहले नया tempपॉइंट संदर्भ बनाते हैं जो संदर्भ जैसे ही स्थान पर इंगित करेगा arg1। फिर आप संदर्भ के समान स्थान arg1पर बिंदु के संदर्भ ले जाते हैं arg2। अंत में एक ही जगह पर इंगितarg2 करेंगे ।temp

यहाँ से के दायरे trickyविधि चला गया है और आप संदर्भ के लिए किसी भी अधिक पहुँच नहीं है: arg1, arg2, tempलेकिन महत्वपूर्ण बात यह है कि जब आप 'जीवन में' होते हैं तो इन संदर्भों के साथ आप जो कुछ भी करते हैं, वह उस वस्तु को स्थायी रूप से प्रभावित करेगा जिस पर वे इंगित करते हैं।

तो विधि को निष्पादित करने के बाद tricky, जब आप वापस आते हैं main, तो आपके पास यह स्थिति है:

तो अब, कार्यक्रम का पूरी तरह से निष्पादन होगा:

X1: 0         Y1: 0
X2: 0         Y2: 0
X1: 100       Y1: 100
X2: 0         Y2: 0

इस मामले की जड़ यह है कि शब्द है संदर्भ अभिव्यक्ति "संदर्भ से गुजरती हैं" में कुछ शब्द का सामान्य अर्थ से पूरी तरह अलग है इसका मतलब संदर्भ जावा में।

जावा में आम तौर पर संदर्भ आ मतलब है एक वस्तु के संदर्भ में । लेकिन प्रोग्रामिंग भाषा सिद्धांत से संदर्भ / मूल्य द्वारा पारित तकनीकी शब्द वेरिएबल धारण करने वाले मेमोरी सेल के संदर्भ के बारे में बात कर रहे हैं , जो कुछ अलग है।


जहां तक ​​मुझे पता है, जावा केवल मूल्य से कॉल जानता है। इसका अर्थ है प्राचीन डेटाटाइप के लिए आप एक प्रतिलिपि के साथ काम करेंगे और वस्तुओं के लिए आप वस्तुओं के संदर्भ की एक प्रति के साथ काम करेंगे। हालांकि मुझे लगता है कि कुछ नुकसान हैं; उदाहरण के लिए, यह काम नहीं करेगा:

public static void swap(StringBuffer s1, StringBuffer s2) {
    StringBuffer temp = s1;
    s1 = s2;
    s2 = temp;
}


public static void main(String[] args) {
    StringBuffer s1 = new StringBuffer("Hello");
    StringBuffer s2 = new StringBuffer("World");
    swap(s1, s2);
    System.out.println(s1);
    System.out.println(s2);
}

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

public static void appendWorld(StringBuffer s1) {
    s1.append(" World");
}

public static void main(String[] args) {
    StringBuffer s = new StringBuffer("Hello");
    appendWorld(s);
    System.out.println(s);
}

यह कमांड लाइन पर हैलो वर्ल्ड को पॉप्युलेट करेगा। यदि आप स्ट्रिंगबफर को स्ट्रिंग में बदलते हैं तो यह सिर्फ हैलो उत्पन्न करेगा क्योंकि स्ट्रिंग अपरिवर्तनीय है। उदाहरण के लिए:

public static void appendWorld(String s){
    s = s+" World";
}

public static void main(String[] args) {
    String s = new String("Hello");
    appendWorld(s);
    System.out.println(s);
}

हालांकि आप इस तरह स्ट्रिंग के लिए एक रैपर बना सकते हैं जो इसे स्ट्रिंग्स के साथ उपयोग करने में सक्षम बनाता है:

class StringWrapper {
    public String value;

    public StringWrapper(String value) {
        this.value = value;
    }
}

public static void appendWorld(StringWrapper s){
    s.value = s.value +" World";
}

public static void main(String[] args) {
    StringWrapper s = new StringWrapper("Hello");
    appendWorld(s);
    System.out.println(s.value);
}

संपादित करें: मेरा मानना ​​है कि यह स्ट्रिंगबफर का उपयोग करने का भी कारण है जब दो स्ट्रिंग्स को "जोड़ने" की बात आती है क्योंकि आप मूल ऑब्जेक्ट को संशोधित कर सकते हैं जिसे आप स्ट्रिंग जैसी अपरिवर्तनीय वस्तुओं के साथ नहीं कर सकते हैं।


जावा में केवल संदर्भ पारित किए जाते हैं और मूल्य से पारित होते हैं:

जावा तर्क सभी मूल्य से पारित होते हैं (विधि द्वारा उपयोग किए जाने पर संदर्भ कॉपी किया जाता है):

आदिम प्रकारों के मामले में, जावा व्यवहार सरल है: मूल्य को आदिम प्रकार के दूसरे उदाहरण में कॉपी किया गया है।

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

व्यवहार आदिम प्रकारों से अलग दिखाई दे सकता है: क्योंकि प्रतिलिपि ऑब्जेक्ट-वेरिएबल में एक ही पता होता है (उसी ऑब्जेक्ट में) ऑब्जेक्ट की सामग्री / सदस्यों को अभी भी एक विधि के भीतर संशोधित किया जा सकता है और बाद में बाहर पहुंच सकता है, जिससे भ्रम होता है जिसमें (युक्त) ऑब्जेक्ट होता है खुद संदर्भ द्वारा पारित किया गया था।

शहरी किंवदंती के लिए "स्ट्रिंग" ऑब्जेक्ट्स एक आदर्श काउंटर-उदाहरण प्रतीत होता है जिसमें कहा गया है कि "ऑब्जेक्ट संदर्भ द्वारा पारित किए जाते हैं":

असल में, एक विधि के भीतर आप कभी भी सक्षम नहीं होंगे, एक स्ट्रिंग के मान को तर्क के रूप में पारित करने के लिए:

एक स्ट्रिंग ऑब्जेक्ट में एक सरणी घोषित फाइनल द्वारा अक्षर होते हैं जिन्हें संशोधित नहीं किया जा सकता है। ऑब्जेक्ट का केवल पता "दूसरे" का उपयोग करके दूसरे स्थान पर बदला जा सकता है। वेरिएबल को अपडेट करने के लिए "नया" का उपयोग करके ऑब्जेक्ट को बाहर से एक्सेस नहीं किया जाएगा, क्योंकि चर प्रारंभ में मूल्य और प्रतिलिपि द्वारा पारित किया गया था।


कंट्रास्ट दिखाने के लिए, निम्न C++ और Java स्निपेट की तुलना करें:

सी ++ में: नोट: खराब कोड - मेमोरी लीक! लेकिन यह बिंदु दर्शाता है।

void cppMethod(int val, int &ref, Dog obj, Dog &objRef, Dog *objPtr, Dog *&objPtrRef)
{
    val = 7; // Modifies the copy
    ref = 7; // Modifies the original variable
    obj.SetName("obj"); // Modifies the copy of Dog passed
    objRef.SetName("objRef"); // Modifies the original Dog passed
    objPtr->SetName("objPtr"); // Modifies the original Dog pointed to 
                               // by the copy of the pointer passed.
    objPtr = new Dog("newObjPtr");  // Modifies the copy of the pointer, 
                                   // leaving the original object alone.
    objPtrRef->SetName("objRefPtr"); // Modifies the original Dog pointed to 
                                    // by the original pointer passed. 
    objPtrRef = new Dog("newObjPtrRef"); // Modifies the original pointer passed
}

int main()
{
    int a = 0;
    int b = 0;
    Dog d0 = Dog("d0");
    Dog d1 = Dog("d1");
    Dog *d2 = new Dog("d2");
    Dog *d3 = new Dog("d3");
    cppMethod(a, b, d0, d1, d2, d3);
    // a is still set to 0
    // b is now set to 7
    // d0 still have name "d0"
    // d1 now has name "objRef"
    // d2 now has name "objPtr"
    // d3 now has name "newObjPtrRef"
}

जावा में,

public static void javaMethod(int val, Dog objPtr)
{
   val = 7; // Modifies the copy
   objPtr.SetName("objPtr") // Modifies the original Dog pointed to 
                            // by the copy of the pointer passed.
   objPtr = new Dog("newObjPtr");  // Modifies the copy of the pointer, 
                                  // leaving the original object alone.
}

public static void main()
{
    int a = 0;
    Dog d0 = new Dog("d0");
    javaMethod(a, d0);
    // a is still set to 0
    // d0 now has name "objPtr"
}

जावा में केवल दो प्रकार के गुजरने हैं: अंतर्निर्मित प्रकारों के मूल्य के आधार पर, और ऑब्जेक्ट प्रकारों के लिए सूचक के मूल्य से।


जावा हमेशा पास-दर-मूल्य होता है । दुर्भाग्य से, उन्होंने किसी ऑब्जेक्ट का स्थान "संदर्भ" कहने का निर्णय लिया। जब हम किसी ऑब्जेक्ट का मान पास करते हैं, तो हम इसका संदर्भ पारित कर रहे हैं। यह शुरुआती लोगों को भ्रमित कर रहा है।

यह इस प्रकार चलता है:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    // we pass the object to foo
    foo(aDog);
    // aDog variable is still pointing to the "Max" dog when foo(...) returns
    aDog.getName().equals("Max"); // true
    aDog.getName().equals("Fifi"); // false 
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // change d inside of foo() to point to a new Dog instance "Fifi"
    d = new Dog("Fifi");
    d.getName().equals("Fifi"); // true
}

aDog.getName() ऊपर दिए गए उदाहरण में अभी भी "Max" वापस आ जाएगा। ऑब्जेक्ट संदर्भ मूल्य द्वारा पारित किया गया है क्योंकि main से मूल्य aDog फ़ंक्शन foo में Dog "Fifi" साथ नहीं बदला जाता है। यदि इसे संदर्भ द्वारा पारित किया गया था, तो main में aDog.getName() कॉल करने के बाद कॉल के बाद "Fifi" लौटाएगा।

इसी तरह:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    foo(aDog);
    // when foo(...) returns, the name of the dog has been changed to "Fifi"
    aDog.getName().equals("Fifi"); // true
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // this changes the name of d to be "Fifi"
    d.setName("Fifi");
}

उपर्युक्त उदाहरण में, Fifi foo(aDog) पर कॉल करने के बाद कुत्ते का नाम है क्योंकि ऑब्जेक्ट का नाम foo(...) अंदर सेट किया गया था। d पर किए जाने वाले किसी भी ऑपरेशन ऐसे हैं कि, सभी व्यावहारिक उद्देश्यों के लिए, वे एक Dog पर ही किए जाते हैं (सिवाय इसके कि जब d को बदल दिया जाता है तो d = new Dog("Boxer") जैसे विभिन्न Dog उदाहरण को इंगित किया जाता है।


जावा केवल मूल्य से गुजर चुका है। इसे प्रमाणित करने के लिए एक बहुत ही सरल उदाहरण।

public void test() {
    MyClass obj = null;
    init(obj);
    //After calling init method, obj still points to null
    //this is because obj is passed as value and not as reference.
}
private void init(MyClass objVar) {
    objVar = new MyClass();
}

भेद, या शायद जिस तरह से मुझे याद है क्योंकि मैं मूल पोस्टर के समान प्रभाव के रूप में उपयोग करता था: जावा हमेशा मूल्य से गुजरता है। जावा में सभी ऑब्जेक्ट्स (जावा में, प्राइमेटिव्स को छोड़कर कुछ भी) संदर्भ हैं। ये संदर्भ मूल्य से पारित होते हैं।


मैंने सोचा कि मैं विनिर्देशों से अधिक विवरण जोड़ने के लिए इस उत्तर का योगदान दूंगा।

सबसे पहले, मूल्य बनाम संदर्भ बनाम गुजरने के बीच क्या अंतर है?

संदर्भ द्वारा उत्तीर्ण करने का अर्थ है बुलाए गए फ़ंक्शंस पैरामीटर कॉलर्स के उत्तीर्ण तर्क के समान होंगे (मूल्य नहीं, लेकिन पहचान - चर स्वयं)।

मूल्य से गुजरने का अर्थ है बुलाए गए फ़ंक्शंस पैरामीटर कॉलर्स के उत्तीर्ण तर्क की एक प्रति होगी।

या विकिपीडिया से, पास-दर-संदर्भ के विषय पर

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

और पास-दर-मूल्य के विषय पर

कॉल-दर-मूल्य में, तर्क अभिव्यक्ति का मूल्यांकन किया जाता है, और परिणामी मान फ़ंक्शन में संबंधित चर के लिए बाध्य होता है [...]। यदि फ़ंक्शन या प्रक्रिया इसके मानकों को मान असाइन करने में सक्षम है, तो केवल इसकी स्थानीय प्रति असाइन की गई है [...]।

दूसरा, हमें यह जानने की जरूरत है कि जावा अपनी विधि के इनवोकेशन में क्या उपयोग करता है। जावा भाषा विशिष्टता राज्यों

जब विधि या कन्स्ट्रक्टर का आह्वान किया जाता है (§15.12), वास्तविक तर्क अभिव्यक्ति के मान विधि या कन्स्ट्रक्टर के शरीर के निष्पादन से पहले, घोषित प्रकार के प्रत्येक, नव निर्मित पैरामीटर चर , आरंभिक प्रारंभ करते हैं।

इसलिए यह तर्क पैरामीटर चर के लिए तर्क के मान को निर्दिष्ट करता है (या बांधता है)।

तर्क का मूल्य क्या है?

आइए संदर्भ प्रकारों पर विचार करें, जावा वर्चुअल मशीन विशिष्टता राज्य

तीन प्रकार के संदर्भ प्रकार हैं : कक्षा प्रकार, सरणी प्रकार, और इंटरफ़ेस प्रकार। उनके मूल्य गतिशील रूप से बनाए गए वर्ग उदाहरण, सरणी, या कक्षा के उदाहरण या सरणी क्रमशः इंटरफेस को लागू करने के संदर्भ हैं।

जावा भाषा विशिष्टता भी कहा गया है

संदर्भ मान (अक्सर संदर्भ) इन वस्तुओं के पॉइंटर्स हैं , और एक विशेष शून्य संदर्भ, जो किसी ऑब्जेक्ट को संदर्भित करता है।

किसी तर्क का मान (कुछ संदर्भ प्रकार का) किसी ऑब्जेक्ट के लिए सूचक होता है। ध्यान दें कि एक चर, संदर्भ प्रकार रिटर्न प्रकार के साथ एक विधि का आविष्कार, और एक उदाहरण निर्माण अभिव्यक्ति ( new ...) सभी संदर्भ प्रकार मान को हल करते हैं।

इसलिए

public void method (String param) {}
...
String var = new String("ref");
method(var);
method(var.toString());
method(new String("ref"));

सभी Stringविधि के नव निर्मित पैरामीटर के उदाहरण के संदर्भ के मान को बाध्य करते हैं param। यह वही है जो पास-दर-मूल्य का वर्णन करता है। इस प्रकार, जावा पास-दर-मूल्य है

तथ्य यह है कि आप किसी विधि को आमंत्रित करने या संदर्भित ऑब्जेक्ट के किसी फ़ील्ड तक पहुंचने के संदर्भ का पालन कर सकते हैं, वार्तालाप के लिए पूरी तरह से अप्रासंगिक है। पास-दर-संदर्भ की परिभाषा थी

इसका आम तौर पर अर्थ यह है कि फ़ंक्शन तर्क के रूप में उपयोग किए जाने वाले चर को संशोधित कर सकता है (यानी असाइन करें) जो कुछ उसके कॉलर द्वारा देखा जाएगा।

जावा में, वैरिएबल को संशोधित करने का मतलब है इसे पुन: असाइन करना। जावा में, यदि आपने विधि के भीतर चर को फिर से सौंप दिया है, तो यह कॉलर पर ध्यान नहीं दिया जाएगा। परिवर्तनीय द्वारा संदर्भित ऑब्जेक्ट को संशोधित करना पूरी तरह से एक अलग अवधारणा है।

जावा वर्चुअल मशीन स्पेसिफिकेशन में आदिम मान भी परिभाषित किए गए here । प्रकार का मान इसी अभिन्न या फ़्लोटिंग पॉइंट मान है, जो उचित रूप से एन्कोड किया गया है (8, 16, 32, 64, आदि बिट्स)।


यह आपको कुछ अंतर्दृष्टि देगा कि जावा वास्तव में इस बिंदु पर कैसे काम करता है कि जावा के संदर्भ में गुजरने या मूल्य से गुजरने के बारे में आपकी अगली चर्चा में आप केवल मुस्कान करेंगे :-)

चरण एक कृपया अपने दिमाग से मिटा दें कि शब्द 'पी' से शुरू होता है "_ _ _ _ _ _ _", खासकर अगर आप अन्य प्रोग्रामिंग भाषाओं से आते हैं। जावा और 'पी' को एक ही पुस्तक, फोरम, या यहां तक ​​कि txt में भी नहीं लिखा जा सकता है।

चरण दो याद रखें कि जब आप किसी ऑब्जेक्ट को किसी विधि में पास करते हैं तो आप ऑब्जेक्ट संदर्भ पास कर रहे हैं, न कि ऑब्जेक्ट स्वयं।

  • छात्र : मास्टर, क्या इसका मतलब यह है कि जावा पास-दर-संदर्भ है?
  • मास्टर : घास का मैदान, संख्या

अब सोचें कि ऑब्जेक्ट का संदर्भ / चर क्या करता है / है:

  1. एक वेरिएबल बिट्स रखता है जो जेवीएम को बताता है कि मेमोरी (हीप) में संदर्भित ऑब्जेक्ट को कैसे प्राप्त किया जाए।
  2. किसी विधि के लिए तर्क पारित करते समय आप संदर्भ चर को पार नहीं कर रहे हैं, लेकिन संदर्भ चर में बिट्स की एक प्रति । ऐसा कुछ: 3bad086a। 3bad086a पारित वस्तु को प्राप्त करने के लिए एक तरीका का प्रतिनिधित्व करता है।
  3. तो आप बस 3bad086a पास कर रहे हैं कि यह संदर्भ का मूल्य है।
  4. आप संदर्भ का मूल्य पारित कर रहे हैं, न कि संदर्भ स्वयं (और वस्तु नहीं)।
  5. यह मान वास्तव में कॉपी किया गया है और विधि को दिया गया है

निम्नलिखित में (कृपया इसे संकलित / निष्पादित करने का प्रयास न करें ...):

1. Person person;
2. person = new Person("Tom");
3. changeName(person);
4.
5. //I didn't use Person person below as an argument to be nice
6. static void changeName(Person anotherReferenceToTheSamePersonObject) {
7.     anotherReferenceToTheSamePersonObject.setName("Jerry");
8. }

क्या होता है?

  • परिवर्तनीय व्यक्ति लाइन # 1 में बनाया गया है और यह शुरुआत में शून्य है।
  • लाइन # 2 में स्मृति में संग्रहीत एक नया व्यक्ति ऑब्जेक्ट बनाया गया है, और परिवर्तनीय व्यक्ति को व्यक्ति ऑब्जेक्ट का संदर्भ दिया जाता है। यही है, इसका पता। मान लें 3bad086a।
  • ऑब्जेक्ट का पता धारण करने वाला परिवर्तनीय व्यक्ति लाइन # 3 में फ़ंक्शन पर भेज दिया जाता है।
  • लाइन # 4 में आप चुप्पी की आवाज सुन सकते हैं
  • लाइन # 5 पर टिप्पणी की जांच करें
  • एक विधि स्थानीय चर - anotherReferenceToTheSamePersonObject - बनाया गया है और फिर जादू # 6 में जादू आता है:
    • परिवर्तनीय / संदर्भ व्यक्ति को बिट-बाय-बिट की प्रतिलिपि बनाई जाती है और फ़ंक्शन के अंदर किसी अन्य संदर्भ में पारित किया जाता है।
    • व्यक्ति के कोई नए उदाहरण नहीं बनाए जाते हैं।
    • दोनों " व्यक्ति " और " anotherReferenceToTheSamePersonObject " 3bad086a के समान मान को पकड़ते हैं।
    • इसे आजमाएं लेकिन व्यक्ति == किसी अन्य संदर्भ के लिए TheSamePersonObject सत्य होगा।
    • दोनों चरों में संदर्भ की पहचान की प्रतियां हैं और वे दोनों एक ही व्यक्ति वस्तु, ढेर पर समान वस्तु और एक प्रतिलिपि नहीं देखते हैं।

एक तस्वीर एक हजार शब्दों के बराबर होती है:

ध्यान दें कि anotherReferenceToTheSamePersonObject तीर ऑब्जेक्ट की ओर निर्देशित है और परिवर्तनीय व्यक्ति की ओर नहीं!

अगर आपको यह नहीं मिला तो बस मुझ पर भरोसा करें और याद रखें कि यह कहना बेहतर है कि जावा मूल्य से गुजरता है । खैर, संदर्भ मूल्य से गुजरें । ओह ठीक है, यहां तक ​​कि पास-बाय-कॉपी-ऑफ-द-वेरिएबल-वैल्यू भी बेहतर है ! ;)

अब मुझे नफरत करने के लिए स्वतंत्र महसूस करें, लेकिन ध्यान दें कि यह देखते हुए विधि तर्कों के बारे में बात करते समय आदिम डेटा प्रकारों और ऑब्जेक्ट्स को पार करने के बीच कोई अंतर नहीं है।

आप हमेशा संदर्भ के मूल्य के बिट्स की एक प्रति पास करते हैं!

  • यदि यह एक आदिम डेटा प्रकार है तो इन बिट्स में आदिम डेटा प्रकार का मूल्य होगा।
  • यदि यह ऑब्जेक्ट है तो बिट्स में उस पते का मान होगा जो JVM को ऑब्जेक्ट को कैसे प्राप्त करें।

जावा पास-बाय-वैल्यू है क्योंकि किसी विधि के अंदर आप संदर्भित ऑब्जेक्ट को जितना चाहें उतना संशोधित कर सकते हैं, लेकिन इससे कोई फर्क नहीं पड़ता कि आप कितनी मेहनत करते हैं, आप कभी भी पारित चर को संशोधित करने में सक्षम नहीं होंगे जो संदर्भ रखेगा (पी _ _ _ नहीं _ _ _ _) वही वस्तु कोई फर्क नहीं पड़ता कि क्या!

उपरोक्त परिवर्तननाम फ़ंक्शन पारित संदर्भ की वास्तविक सामग्री (बिट मान) को संशोधित करने में सक्षम नहीं होगा। दूसरे शब्द में परिवर्तननाम व्यक्ति व्यक्ति को किसी अन्य वस्तु का संदर्भ नहीं दे सकता है।

बेशक आप इसे छोटा कर सकते हैं और बस कहें कि जावा पास-दर-मूल्य है!


मैंने hereकिसी भी प्रोग्रामिंग भाषाओं के लिए इस तरह के प्रश्नों के लिए समर्पित धागा बनाया here

जावा का भी उल्लेख है । संक्षिप्त सारांश यहां दिया गया है:

  • जावा मूल्य के आधार पर पैरामीटर पास करता है
  • "मूल्य से" जावा में एक विधि को पैरामीटर पास करने का एकमात्र तरीका है
  • पैरामीटर के रूप में दिए गए ऑब्जेक्ट से विधियों का उपयोग करने से ऑब्जेक्ट को संदर्भित किया जाएगा क्योंकि संदर्भ मूल ऑब्जेक्ट्स को इंगित करते हैं। (यदि वह विधि स्वयं कुछ मान बदलती है)




pass-by-value