java - जावा में संदर्भ द्वारा एक स्ट्रिंग पास?




c string (9)

मैं C में निम्नलिखित करने के लिए प्रयोग किया जाता C :

void main() {
    String zText = "";
    fillString(zText);
    printf(zText);
}

void fillString(String zText) {
    zText += "foo";
}

और आउटपुट है:

foo

हालांकि, जावा में, यह काम नहीं लग रहा है। मुझे लगता है कि संदर्भित द्वारा पारित होने के बजाय String ऑब्जेक्ट की प्रतिलिपि बनाई गई है । मैंने सोचा था कि स्ट्रिंग्स ऑब्जेक्ट्स थे, जिन्हें संदर्भ द्वारा हमेशा पारित किया जाता है।

यहाँ क्या हो रहा है?


java.lang.String अपरिवर्तनीय है।

मुझे यूआरएल चिपकाने से नफरत है, लेकिन अगर आप जावा-भूमि में हैं तो पढ़ने और समझने के लिए http://java.sun.com/javase/6/docs/api/java/lang/String.html आवश्यक है


आपके पास तीन विकल्प हैं:

  1. एक स्ट्रिंगबिल्डर का प्रयोग करें:

    StringBuilder zText = new StringBuilder ();
    void fillString(StringBuilder zText) { zText.append ("foo"); }
    
  2. कंटेनर क्लास बनाएं और अपनी विधि में कंटेनर का एक उदाहरण पास करें:

    public class Container { public String data; }
    void fillString(Container c) { c.data += "foo"; }
    
  3. एक सरणी बनाएं:

    new String[] zText = new String[1];
    zText[0] = "";
    
    void fillString(String[] zText) { zText[0] += "foo"; }
    

एक प्रदर्शन बिंदु से, स्ट्रिंगबिल्डर आमतौर पर सबसे अच्छा विकल्प होता है।


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

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

void foo( object o )
{
    o = new Object( );  // original reference still points to old value on the heap
}

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


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

स्ट्रिंग एक आदिम नहीं है, यह एक वस्तु है, और यह वस्तु का एक विशेष मामला है।

यह स्मृति-बचत उद्देश्य के लिए है। जेवीएम में, एक स्ट्रिंग पूल है। बनाई गई प्रत्येक स्ट्रिंग के लिए, जेवीएम यह देखने का प्रयास करेगा कि स्ट्रिंग पूल में एक ही स्ट्रिंग मौजूद है या नहीं, और अगर पहले से ही कोई है तो उसे इंगित करें।

public class TestString
{
    private static String a = "hello world";
    private static String b = "hello world";
    private static String c = "hello " + "world";
    private static String d = new String("hello world");

    private static Object o1 = new Object();
    private static Object o2 = new Object();

    public static void main(String[] args)
    {
        System.out.println("a==b:"+(a == b));
        System.out.println("a==c:"+(a == c));
        System.out.println("a==d:"+(a == d));
        System.out.println("a.equals(d):"+(a.equals(d)));
        System.out.println("o1==o2:"+(o1 == o2));

        passString(a);
        passString(d);
    }

    public static void passString(String s)
    {
        System.out.println("passString:"+(a == s));
    }
}

/ * आउटपुट * /

a==b:true
a==c:true
a==d:false
a.equals(d):true
o1==o2:false
passString:true
passString:false

== स्मृति पता (संदर्भ) की जांच कर रहा है, और .equals सामग्री (मान) की जांच कर रहा है


स्ट्रिंग जावा में immutable हैं।


स्ट्रिंग जावा में एक अपरिवर्तनीय वस्तु है। आप जिस कार्य को पूरा करने की कोशिश कर रहे हैं, वह करने के लिए आप स्ट्रिंगबिल्डर वर्ग का उपयोग कर सकते हैं, निम्नानुसार:

public static void main(String[] args)
{
    StringBuilder sb = new StringBuilder("hello, world!");
    System.out.println(sb);
    foo(sb);
    System.out.println(sb);

}

public static void foo(StringBuilder str)
{
    str.delete(0, str.length());
    str.append("String has been modified");
}

एक और विकल्प स्ट्रिंग के साथ एक वर्ग को एक स्कोप वेरिएबल (अत्यधिक हतोत्साहित) के रूप में निम्नानुसार बनाना है:

class MyString
{
    public String value;
}

public static void main(String[] args)
{
    MyString ms = new MyString();
    ms.value = "Hello, World!";

}

public static void foo(MyString str)
{
    str.value = "String has been modified";
}

स्ट्रिंग जावा में एक विशेष कक्षा है। यह थ्रेड सेव है जिसका अर्थ है "एक बार स्ट्रिंग इंस्टेंस बनने के बाद, स्ट्रिंग इंस्टेंस की सामग्री कभी नहीं बदली जाएगी"।

यहां क्या चल रहा है

 zText += "foo";

सबसे पहले, जावा कंपाइलर को zText स्ट्रिंग इंस्टेंस का मान मिलेगा, फिर एक नया स्ट्रिंग इंस्टेंस बनाएं जिसका मान zText "foo" जोड़ रहा है। तो आप जानते हैं कि zText इंगित करने का उदाहरण क्यों नहीं बदला जाता है। यह पूरी तरह से एक नया उदाहरण है। वास्तव में, यहां तक ​​कि स्ट्रिंग "फू" एक नया स्ट्रिंग उदाहरण है। इसलिए, इस कथन के लिए, जावा दो स्ट्रिंग उदाहरण बनाएगा, एक "foo" है, दूसरा zText "foo" जोड़ना है। नियम सरल है: स्ट्रिंग इंस्टेंस का मान कभी नहीं बदला जाएगा।

विधि fillString के लिए, आप स्ट्रिंगबफर को पैरामीटर के रूप में उपयोग कर सकते हैं, या आप इसे इस तरह बदल सकते हैं:

String fillString(String zText) {
    return zText += "foo";
}

हारून दिगुल्ला का अब तक का सबसे अच्छा जवाब है। उनके दूसरे विकल्प का एक बदलाव है रैपर या कंटेनर क्लास का उपयोग करना कॉमन्स लैंग लाइब्रेरी संस्करण 3+ के MutableObject:

void fillString(MutableObject<String> c) { c.setValue(c.getValue() + "foo"); }

आप कंटेनर वर्ग की घोषणा को बचाते हैं। कमी कॉमन्स lang lib के लिए एक निर्भरता है। लेकिन lib में काफी उपयोगी कार्य है और लगभग किसी भी बड़े प्रोजेक्ट में मैंने इसका इस्तेमाल किया है।





pass-by-value