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





15 Answers

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

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

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

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 असाइन किया था।

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

java methods parameter-passing pass-by-reference pass-by-value

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

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

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

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




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

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

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

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

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




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

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




कंट्रास्ट दिखाने के लिए, निम्न 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"
}

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




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

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




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

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




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

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

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

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

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

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




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

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

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




मुझे चार उदाहरणों की मदद से मेरी समझ समझाने की कोशिश करें। जावा पास-दर-मान है, और पास-दर-संदर्भ नहीं है

/ **

मूल्य से गुजरें

जावा में, सभी पैरामीटर मान द्वारा पारित होते हैं, यानी एक विधि तर्क असाइन करना कॉलर को दिखाई नहीं देता है।

* /

उदाहरण 1:

public class PassByValueString {
    public static void main(String[] args) {
        new PassByValueString().caller();
    }

    public void caller() {
        String value = "Nikhil";
        boolean valueflag = false;
        String output = method(value, valueflag);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'value' and 'valueflag'
         */
        System.out.println("output : " + output);
        System.out.println("value : " + value);
        System.out.println("valueflag : " + valueflag);

    }

    public String method(String value, boolean valueflag) {
        value = "Anand";
        valueflag = true;
        return "output";
    }
}

परिणाम

output : output
value : Nikhil
valueflag : false

उदाहरण 2:

/ ** * * मूल्य से पास * * /

public class PassByValueNewString {
    public static void main(String[] args) {
        new PassByValueNewString().caller();
    }

    public void caller() {
        String value = new String("Nikhil");
        boolean valueflag = false;
        String output = method(value, valueflag);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'value' and 'valueflag'
         */
        System.out.println("output : " + output);
        System.out.println("value : " + value);
        System.out.println("valueflag : " + valueflag);

    }

    public String method(String value, boolean valueflag) {
        value = "Anand";
        valueflag = true;
        return "output";
    }
}

परिणाम

output : output
value : Nikhil
valueflag : false

उदाहरण 3:

/ ** यह 'मूल्य से गुजरना' संदर्भ द्वारा पास 'की भावना है

कुछ लोग कहते हैं कि आदिम प्रकार और 'स्ट्रिंग' 'मूल्य से गुजरते हैं' और वस्तुएं 'संदर्भ द्वारा पास' होती हैं।

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

* /

public class PassByValueObjectCase1 {

    private class Student {
        int id;
        String name;
        public Student() {
        }
        public Student(int id, String name) {
            super();
            this.id = id;
            this.name = name;
        }
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        @Override
        public String toString() {
            return "Student [id=" + id + ", name=" + name + "]";
        }
    }

    public static void main(String[] args) {
        new PassByValueObjectCase1().caller();
    }

    public void caller() {
        Student student = new Student(10, "Nikhil");
        String output = method(student);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'student'
         */
        System.out.println("output : " + output);
        System.out.println("student : " + student);
    }

    public String method(Student student) {
        student.setName("Anand");
        return "output";
    }
}

परिणाम

output : output
student : Student [id=10, name=Anand]

उदाहरण 4:

/ **

उदाहरण 3 (PassByValueObjectCase1.java) में जो उल्लेख किया गया था, उसके अलावा, हम मूल दायरे के बाहर वास्तविक संदर्भ नहीं बदल सकते हैं। "

नोट: मैं कोड चिपकाना नहीं कर रहा हूं private class StudentStudentउदाहरण के लिए कक्षा परिभाषा उदाहरण 3 के समान है।

* /

public class PassByValueObjectCase2 {

    public static void main(String[] args) {
        new PassByValueObjectCase2().caller();
    }

    public void caller() {
        // student has the actual reference to a Student object created
        // can we change this actual reference outside the local scope? Let's see
        Student student = new Student(10, "Nikhil");
        String output = method(student);
        /*
         * 'output' is insignificant in this example. we are more interested in
         * 'student'
         */
        System.out.println("output : " + output);
        System.out.println("student : " + student); // Will it print Nikhil or Anand?
    }

    public String method(Student student) {
        student = new Student(20, "Anand");
        return "output";
    }

}

परिणाम

output : output
student : Student [id=10, name=Nikhil]



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

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

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

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

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




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




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

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



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

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 का उत्पादन:

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

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




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

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

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

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

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




जावा VALUE द्वारा मानकों को पास करता है, और केवल मूल्य से ।

लंबी कहानी को कम करने के लिए:

सी # से आने वाले लोगों के लिए: वहां कोई "आउट" पैरामीटर नहीं है।

पास्कल से आने वाले लोगों के लिए: कोई "var" पैरामीटर नहीं है

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

एक वर्कअराउंड StringBuilderइसके बजाय पैरामीटर का उपयोग करना है String। और आप हमेशा सरणी का उपयोग कर सकते हैं!




Related