191

I am used to doing the following in C:

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

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

And the output is:

foo

However, in Java, this does not seem to work. I assume because the String object is copied instead of passed by referenced. I thought Strings were objects, which are always passed by reference.

What is going on here?

Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
Soren Johnson
  • 2,561
  • 4
  • 21
  • 17

15 Answers15

231

You have three options:

  1. Use a StringBuilder:

    StringBuilder zText = new StringBuilder ();
    void fillString(StringBuilder zText) { zText.append ("foo"); }
    
  2. Create a container class and pass an instance of the container to your method:

    public class Container { public String data; }
    void fillString(Container c) { c.data += "foo"; }
    
  3. Create an array:

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

From a performance point of view, the StringBuilder is usually the best option.

GMeister
  • 331
  • 2
  • 15
Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
  • 12
    Just keep in mind that StringBuilder is not thread safe. In multithreaded environment use the StringBuffer class or manually take care of synchronization. – Boris Pavlović Aug 13 '09 at 08:59
  • 15
    @Boris Pavlovic - yes, but unless the same StringBuilder is used by different threads, which IMO is unlikely in any given scenario, it should not be a problem. It doesn't matter if the method is called by different threads, receiving different StringBuilder. – Ravi Wallau Aug 13 '09 at 18:37
  • I like the third option (array) the most because it is the only general one. It allows also passing boolean, int, etc. Can you please explain a little bit more in details the performance of this solution - you claim that the first one is better from a performance point of view. – meolic Apr 26 '19 at 06:52
  • 1
    @meolic `StringBuilder` is faster that `s += "..."` since it doesn't allocate new String objects every time. In fact when you write Java code like `s = "Hello " + name`, then the compiler will generate byte code which creates a `StringBuilder` and calls `append()` two times. – Aaron Digulla May 07 '19 at 13:36
99

In Java nothing is passed by reference. Everything is passed by value. Object references are passed by value. Additionally Strings are immutable. So when you append to the passed String you just get a new String. You could use a return value, or pass a StringBuffer instead.

nbro
  • 15,395
  • 32
  • 113
  • 196
zedoo
  • 10,562
  • 12
  • 44
  • 55
  • 2
    This is not a misconception it's a concept – Patrick Cornelissen Aug 13 '09 at 08:31
  • 48
    Ummm..no, it is a misconception that you are passing an object by reference. You are passing a reference by value. – Ed S. Aug 13 '09 at 08:32
  • 35
    Yes, it's a misconception. It's a huge, widespread misconception. It leads to an interview question I hate: ("how does Java pass arguments"). I hate it because roughly half of the interviewers actually seem to want the wrong answer ("primitives by value, objects by reference"). The right answer takes longer to give, and seems to confuse some of them. And they won't be convinced: I swear I flunked a tech screen because the CSMajor-type screener had heard the misconception in college and believed it as gospel. Feh. – CPerkins Aug 13 '09 at 14:34
  • 4
    @CPerkins You have no idea how angry that makes me. It makes my blood boil. –  Aug 14 '09 at 00:01
  • Maybe I just used the wrong word, non native english speaker. I probably meant misunderstanding. – zedoo Feb 18 '12 at 17:37
  • 1
    Thumbs up for pointing out the String is immutable! That's the key reason why a StringBuffer works while String not – spiralmoon Jan 05 '14 at 05:15
  • 9
    Everything is passed by value in all languages. Some of those values happen to be addresses (references). – gerardw Nov 20 '14 at 20:30
  • 3
    Is this a completely pedantic point, or am I missing something? I do not understand the upvotes on this answer. It just confuses the issue. – bnieland Apr 27 '16 at 14:59
  • I agree with bnieland. In conversation, when someone says "passed by reference", we all understand their effective meaning, even if their technical accuracy is askew. – Syndog Oct 25 '17 at 19:15
  • This is incorrect! In Java almost everything passed by reference! Except stack types, String. If you mean that the pointer to Objected passed as value the same thing happens in other languages and called "passed by reference" not "value"! – Yan Jan 09 '18 at 11:04
56

What is happening is that the reference is passed by value, i.e., a copy of the reference is passed. Nothing in java is passed by reference, and since a string is immutable, that assignment creates a new string object that the copy of the reference now points to. The original reference still points to the empty string.

This would be the same for any object, i.e., setting it to a new value in a method. The example below just makes what is going on more obvious, but concatenating a string is really the same thing.

void foo( object o )
{
    o = new Object( );  // original reference still points to old value on the heap
}
Ed S.
  • 122,712
  • 22
  • 185
  • 265
  • Actually "the copy of the reference" is overwritten be a new reference. But only for the local variable. The called variable is still holding the old reference. – Humpity Feb 25 '23 at 21:46
18

java.lang.String is immutable.

I hate pasting URLs but https://docs.oracle.com/javase/10/docs/api/java/lang/String.html is essential for you to read and understand if you're in java-land.

jcroskery
  • 135
  • 2
  • 10
Ryan Fernandes
  • 8,238
  • 7
  • 36
  • 53
  • I don't think Soren Johnson doesn't know what a String is, that was not the question and not what I came looking for here after 15 years of Java. – AHH Jul 14 '17 at 10:16
12

All arguments in Java are passed by value. When you pass a String to a function, the value that's passed is a reference to a String object, but you can't modify that reference, and the underlying String object is immutable.

The assignment

zText += foo;

is equivalent to:

zText = new String(zText + "foo");

That is, it (locally) reassigns the parameter zText as a new reference, which points to a new memory location, in which is a new String that contains the original contents of zText with "foo" appended.

The original object is not modified, and the main() method's local variable zText still points to the original (empty) string.

class StringFiller {
  static void fillString(String zText) {
    zText += "foo";
    System.out.println("Local value: " + zText);
  }

  public static void main(String[] args) {
    String zText = "";
    System.out.println("Original value: " + zText);
    fillString(zText);
    System.out.println("Final value: " + zText);
  }
}

prints:

Original value:
Local value: foo
Final value:

If you want to modify the string, you can as noted use StringBuilder or else some container (an array or an AtomicReference or a custom container class) that gives you an additional level of pointer indirection. Alternatively, just return the new value and assign it:

class StringFiller2 {
  static String fillString(String zText) {
    zText += "foo";
    System.out.println("Local value: " + zText);
    return zText;
  }

  public static void main(String[] args) {
    String zText = "";
    System.out.println("Original value: " + zText);
    zText = fillString(zText);
    System.out.println("Final value: " + zText);
  }
}

prints:

Original value:
Local value: foo
Final value: foo

This is probably the most Java-like solution in the general case -- see the Effective Java item "Favor immutability."

As noted, though, StringBuilder will often give you better performance -- if you have a lot of appending to do, particularly inside a loop, use StringBuilder.

But try to pass around immutable Strings rather than mutable StringBuilders if you can -- your code will be easier to read and more maintainable. Consider making your parameters final, and configuring your IDE to warn you when you reassign a method parameter to a new value.

David Moles
  • 48,006
  • 27
  • 136
  • 235
  • 6
    You say "strictly speaking" as if it's almost irrelevant - whereas it lies at the heart of the problem. If Java *really* had pass by reference, it wouldn't be a problem. *Please* try to avoid propagating the myth of "Java passes objects by reference" - explaining the (correct) "references are passed by value" model is a lot more helpful, IMO. – Jon Skeet Aug 13 '09 at 08:53
  • Hey, where did my previous comment go? :-) As I said before, and as Jon has now added, the real issue here is that the reference is passed by value. That is very important and many people do not understand the semantic difference. – Ed S. Aug 13 '09 at 16:55
  • 2
    Fair enough. As a Java programmer I haven't seen anything *actually* passed by reference in better than a decade, so my language has gotten sloppy. But you're right, I should have taken more care, especially for a C-programming audience. – David Moles Aug 14 '09 at 08:22
  • This answer clearly explains the concept of immutable string object, passed by value, passed by reference, passed by value of a reference. Thanks. – Denny Hsu Aug 01 '22 at 23:41
8

objects are passed by reference, primitives are passed by value.

String is not a primitive, it is an object, and it is a special case of object.

This is for memory-saving purpose. In JVM, there is a string pool. For every string created, JVM will try to see if the same string exist in the string pool, and point to it if there were already one.

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

/* OUTPUT */

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

the == is checking for memory address (reference), and the .equals is checking for contents (value)

sanjeevprasad
  • 804
  • 1
  • 6
  • 21
janetsmith
  • 8,562
  • 11
  • 58
  • 76
  • 3
    Not entirely true. Java is always pass by VALUE; the trick is that Objects are passed by *reference* which are passes by value. (tricky, I know). Check this one: http://javadude.com/articles/passbyvalue.htm – adhg Jun 17 '15 at 22:27
  • 2
    @adhg you wrote " Objects are passed by reference which are passes by value." <--- that is gibberish. You mean objects have references which are passed by value – barlop Jan 03 '16 at 03:22
  • 2
    -1 You wrote "objects are passed by reference," <-- FALSE. If that were true then when calling a method and passing a variable, the method could make the variable point/refer to a different object, but it cannot. Everything is passed by value in java. And you still see the effect of changing the attributes of an object, outside of the method. – barlop Jan 03 '16 at 03:23
  • see the answer here http://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value – barlop Jan 05 '16 at 06:15
6

String is an immutable object in Java. You can use the StringBuilder class to do the job you're trying to accomplish, as follows:

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

Another option is to create a class with a String as a scope variable (highly discouraged) as follows:

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

String is a special class in Java. It is Thread Safe which means "Once a String instance is created, the content of the String instance will never changed ".

Here is what is going on for

 zText += "foo";

First, Java compiler will get the value of zText String instance, then create a new String instance whose value is zText appending "foo". So you know why the instance that zText point to does not changed. It is totally a new instance. In fact, even String "foo" is a new String instance. So, for this statement, Java will create two String instance, one is "foo", another is the value of zText append "foo". The rule is simple: The value of String instance will never be changed.

For method fillString, you can use a StringBuffer as parameter, or you can change it like this:

String fillString(String zText) {
    return zText += "foo";
}
user961954
  • 3,214
  • 2
  • 17
  • 24
DeepNightTwo
  • 4,809
  • 8
  • 46
  • 60
2

The answer is simple. In java strings are immutable. Hence its like using 'final' modifier (or 'const' in C/C++). So, once assigned, you cannot change it like the way you did.

You can change which value to which a string points, but you can NOT change the actual value to which this string is currently pointing.

Ie. String s1 = "hey". You can make s1 = "woah", and that's totally ok, but you can't actually change the underlying value of the string (in this case: "hey") to be something else once its assigned using plusEquals, etc. (ie. s1 += " whatup != "hey whatup").

To do that, use the StringBuilder or StringBuffer classes or other mutable containers, then just call .toString() to convert the object back to a string.

note: Strings are often used as hash keys hence that's part of the reason why they are immutable.

bnieland
  • 6,047
  • 4
  • 40
  • 66
ak_2050
  • 159
  • 4
  • Whether it is mutable or immutable is not relevant. `=` on a reference NEVER affects the object it used to point to, regardless of class. – newacct Apr 08 '14 at 06:13
1

Strings are immutable in Java.

Grzegorz Oledzki
  • 23,614
  • 16
  • 68
  • 106
1

This works use StringBuffer

public class test {
 public static void main(String[] args) {
 StringBuffer zText = new StringBuffer("");
    fillString(zText);
    System.out.println(zText.toString());
 }
  static void fillString(StringBuffer zText) {
    zText .append("foo");
}
}

Even better use StringBuilder

public class test {
 public static void main(String[] args) {
 StringBuilder zText = new StringBuilder("");
    fillString(zText);
    System.out.println(zText.toString());
 }
  static void fillString(StringBuilder zText) {
    zText .append("foo");
}
}
Mohammed Rafeeq
  • 2,586
  • 25
  • 26
1

String is immutable in java. you cannot modify/change, an existing string literal/object.

String s="Hello"; s=s+"hi";

Here the previous reference s is replaced by the new refernce s pointing to value "HelloHi".

However, for bringing mutability we have StringBuilder and StringBuffer.

StringBuilder s=new StringBuilder(); s.append("Hi");

this appends the new value "Hi" to the same refernce s. //

gauravJ
  • 41
  • 1
  • 4
0

Aaron Digulla has the best answer so far. A variation of his second option is to use the wrapper or container class MutableObject of the commons lang library version 3+:

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

you save the declaration of the container class. The drawback is a dependency to the commons lang lib. But the lib has quite a lot of useful function and almost any larger project i have worked on used it.

0

For passing an object (including String) by reference in java, you might pass it as member of a surrounding adapter. A solution with a generic is here:

import java.io.Serializable;

public class ByRef<T extends Object> implements Serializable
{
    private static final long serialVersionUID = 6310102145974374589L;

    T v;

    public ByRef(T v)
    {
        this.v = v;
    }

    public ByRef()
    {
        v = null;
    }

    public void set(T nv)
    {
        v = nv;
    }

    public T get()
    {
        return v;
    }

// ------------------------------------------------------------------

    static void fillString(ByRef<String> zText)
    {
        zText.set(zText.get() + "foo");
    }

    public static void main(String args[])
    {
        final ByRef<String> zText = new ByRef<String>(new String(""));
        fillString(zText);
        System.out.println(zText.get());
    }
}
Sam Ginrich
  • 661
  • 6
  • 7
0

For someone who are more curious

class Testt {
    static void Display(String s , String varname){
        System.out.println(varname + " variable data = "+ s + " :: address hashmap =  " + s.hashCode());
    }

    static void changeto(String s , String t){
        System.out.println("entered function");
        Display(s , "s");
        s = t ;
        Display(s,"s");
        System.out.println("exiting function");
    }

    public static void main(String args[]){
        String s =  "hi" ;
        Display(s,"s");
        changeto(s,"bye");
        Display(s,"s");
    }
}

Now by running this above code you can see how address hashcodes change with String variable s . a new object is allocated to variable s in function changeto when s is changed

Dinesh Shingadiya
  • 988
  • 1
  • 8
  • 23