tags:

views:

1222

answers:

8

I am used to doing the following in C:

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

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

output:

    foo

However, in Java, this does not seem to work; I assume because the String is copied instead of passed by referenced. I thought Strings were Objects, which are always passed by reference (so to speak) in Java? What is going on here? thanks...

+10  A: 

java.lang.String is immutable.

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

Ryan Fernandes
A: 

Strings are immutable in Java.

Grzegorz Oledzki
+14  A: 

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.

Aaron Digulla
+1 for the first option
Maurice Perry
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ć
@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
+11  A: 

Nah, an old misconception. 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.

zedoo
This is not a misconception it's a concept
Patrick Cornelissen
Ummm..no, it is a misconception that you are passing an object by reference. You are passing a reference by value.
Ed Swangren
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
@CPerkins You have no idea how angry that makes me. It makes my blood boil.
Anthony Cuozzo
+4  A: 

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 Swangren
+1  A: 

Strings are passed by reference (well, strictly speaking, as Ed Swangren notes, a reference to the string is passed by value) but they're immutable.

zText += foo;

is equivalent to

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

That is, it modifies the (local) variable zText such that it now points to a new memory location, in which is a String with 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 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 StringfillString(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
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
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 Swangren
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
A: 

String is a special class in Java. It is Thread Save 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";
}
DeepNightTwo
... though if you were to define 'fillString' as per this code, you *really* should give it a more appropriate name!
Stephen C
Yes. This method should be named to "appendString" or so.
DeepNightTwo
+1  A: 

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();
    /**
     * @param args
     */
    public static void main(String[] args)
    {
     // TODO Auto-generated method stub


     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

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

janetsmith