简体   繁体   中英

Why does the following example seem to refute that Strings are immutable objects in Java?

I am using the OpenJDK Java compiler under Ubuntu. I wanted to convert a character array to a string and when that seemed to have ended up giving ambiguous results, I tried to write a toString method of my own. In the process, I wrote a test program wherein (out of the fun of it) I tried to compile the following code.

class toString{
    public static void main(String[] args){
        string = "abc";
        string = string + "bcd";
        System.out.println(string);
    }
}

Now, I know that String objects in Java are immutable and the code should have in fact generated an error but to my surprise, it printed abcbcd to the console. Does this mean that String objects in Java are mutable or is there something wrong with the implementation of OpenJDK compiler in this case?

The code that you've posted above does not actually mutate any strings, though it looks like it does. The reason is that this line doesn't mutate the string:

string = string + "bcd";

Instead, what this does is:

  1. Construct a new string whose value is string + "bcd" .
  2. Change what string is referenced by string to refer to this new string.

In other words, the actual concrete string objects themselves weren't changed, but the references to those strings were indeed modified. Immutability in Java usually means that objects cannot be modified, not the references to those objects.

An important detail that confuses a lot of new Java programmers is that the above line is often written as

string += "bcd";

which looks even more strongly as though it's concatenating bcd onto the end of the string and thereby mutating it, even though it's equivalent to the above code and therefore doesn't cause any changes to the actual String object (again, it works by creating a new String object and changing what object the reference refers to.)

To see that what's going on here is that you're actually changing the reference and not the string it refers to, you can try rewriting the code to make string final , which prevents you from changing what object is referenced. If you do so, you'll find that the code no longer compiles. For example:

class toString{
    public static void main(String[] args){
        final String string = "abc";
        string = string + "bcd";    // Error: can't change string!
        System.out.println(string);
    }
}

One final note - another common cause of grief for new Java programmers when using String s is that String has methods that appear to mutate the string but in actuality do not. For example, this code does not work correctly:

String s = "HELLO, WORLD!";
s.toLowerCase(); // Legal but incorrect
System.out.println(s); // Prints HELLO, WORLD!

Here, the call to s.toLowerCase() does not actually convert the characters of the string to lower case, but instead produces a new string with the characters set to lower case. If you then rewrite the code as

String s = "HELLO, WORLD!";
s = s.toLowerCase();   // Legal and correct
System.out.println(s); // Prints hello, world!

Then the code will behave properly. Again, the key detail here is that the assignment to s does not change any concrete String object, but just adjusts what object s refers to.

Hope this helps!

Nope, no error - you're not changing the contents of any string object.

You're changing the value of a string variable which is entirely different. Look at this as two operations:

  • Creating a new string, the result of the expression string + "bcd"
  • Assigning the reference to the new string back to the string variable

Let's separate them out explicitly:

String string = "abc";
String other = string + "bcd";

// abc - neither the value of string nor the object's contents have changed
System.out.println(string); 

// This is *just* changing the value of the string variable. It's not making
// any changes to the data within any objects.
string = other;

It's very important to distinguish between variables and objects . The value of a variable is only ever a reference or a primitive type value. Changing the value of a variable does not change the contents of the object to which it previously referred.

The difference is between References to an object and the object itself.

String XXX = "xxx";

Means: Make a new variable and assign the reference to an instance of object String that contains the literal string "xxx".

XXX = XXX + "yyy";

Means:

Get the reference to the object we have in variable named XXX. Make a new object of type String that contains the string literal "yyy". Add them together executing the string + operator. This operation will create a new String object that contains the literal string "xxxyyy". After all this, we put the reference to the new object again in the variable XXX.

The old referenced object containing "xxx" is not used anymore but the content of it was never modified.

As a counter proof, there is an example:

String a = "abc";
String b = "def";
String c = a;

a = a + b;

System.out.println(a); // will print "abcdef".
System.out.println(b); // will print "def".
System.out.println(c); // will print "abc".

// Now we compare references, in java == operator compare references, not the content of objects.

System.out.println(a == a); // Will print true
System.out.println(a == c); // Will print false, objects are not the same!

a = c;

System.out.println(a == c); // Will print true, now a and b points on the same instance.

Object instance are something "abstract" that live in a portion of memory of your program. A reference variable is a reference to that portion of memory. You can access objects only through variables (or return values).

A single object can have more than one variable pointing at it.

String is immutable means that you cannot modify the content of that area of memory used by the String object, but of course, you are free to change and exchange references to it as you prefer.

It doesn't refute it. It actually won't compile since string isn't declared as a String object. But, let's say you meant:

class toString{
    public static void main(String[] args){
        String string = "abc";
        string = string + "bcd";
        System.out.println(string);
    }
}

See the + operator creates a new String leaving "abc" in tact. The original "abc" still exists, but all you've actually done is create a new string "abcbcd" and overwrite the original reference to "abc" when doing: string = string + "bcd". If you changed that code to this you'll see what I mean:

class toString {
    public static void main(String[] args ) {
        String originalString = "abc";
        String newString = originalString + "bcd";

        System.out.println( originalString );  // prints the original "abc";
        System.out.println( newString );       // prints the new string "abcbcd";
    }
}

String objects are immutable, you are simply re-assigning the value of string to string + "bcd" in your code example. You are not modifying the existing String object, you are creating a new one and assigning it to the old name.

string = string + "bcd" sets a new instance of String to the variable string , rather than modify that object

what this will do is that it will make a new object, and replace the old one. if you want mutable string look at string builder.

The String variable string itself is mutable.

The String object "abc" is immutable, as is the String object "bcd" , and the result of the concatenation, "abcbcd" . That last result is assigned to the variable.

No String was mutated in the execution of that code snipped.

The string is immutable... all your example does is show that you can assign a new string reference to a variable.

If we make the code compile, and change it slightly:

class toString{
    public static void main(String[] args){
        String string = "abc";
        System.out.println(string);
        string = string + "bcd";
        System.out.println(string);
    }
}

You will see "abc" and then "abcbcd" which, could lead you to think that the string has changed, but it has not.

When you do the string = /* whatever */ you are overwriting what used to be in the variable called string with a new value.

If String had a method, such as setCharAt(int index, char value) then it would be mutable.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM