This is a classic question of Java. Many similar questions have been asked on stackoverflow, and there are a lot of incorrect/incomplete answers. The question is simple if you don’t think too much. But it could be very confusing, if you give more thought to it.
1. A code fragment that is interesting & confusing
public static void main(String[] args) { String x = new String("ab"); change(x); System.out.println(x); } public static void change(String x) { x = "cd"; } |
It prints “ab”.
In C++, the code is as follows:
void change(string &x) { x = "cd"; } int main(){ string x = "ab"; change(x); cout << x << endl; } |
it prints “cd”.
2. Common confusing questions
x stores the reference which points to the “ab” string in the heap. So when x is passed as a parameter to the change() method, it still points to the “ab” in the heap like the following:
Because java is pass-by-value, the value of x is the reference to “ab”. When the method change() gets invoked, it creates a new “cd” object, and x now is pointing to “cd” like the following:
It seems to be a pretty reasonable explanation. They are clear that Java is always pass-by-value. But what is wrong here?
3. What the code really does?
The explanation above has several mistakes. To understand this easily, it is a good idea to briefly walk though the whole process.
When the string “ab” is created, Java allocates the amount of memory required to store the string object. Then, the object is assigned to variable x, the variable is actually assigned a reference to the object. This reference is the address of the memory location where the object is stored.
The variable x contains a reference to the string object. x is not a reference itself! It is a variable that stores a reference(memory address).
Java is pass-by-value ONLY. When x is passed to the change() method, a copy of value of x (a reference) is passed. The method change() creates another object “cd” and it has a different reference. It is the variable x that changes its reference(to “cd”), not the reference itself.
The following diagram shows what it really does.
4. The wrong explanation
The problem raised from the first code fragment is nothing related with string immutability. Even if String is replaced with StringBuilder, the result is still the same. The key point is that variable stores the reference, but is not the reference itself!
5. Solution to this problem
If we really need to change the value of the object. First of all, the object should be changeable, e.g., StringBuilder. Secondly, we need to make sure that there is no new object created and assigned to the parameter variable, because Java is passing-by-value only.
public static void main(String[] args) { StringBuilder x = new StringBuilder("ab"); change(x); System.out.println(x); } public static void change(StringBuilder x) { x.delete(0, 2).append("cd"); } |
What are you even talking about, you fail to completely understand the whole point of the article. Say you take an analogy of phone number (ie they are the references) and a piece of paper being the variable that can store there’s references. Say there are two service providers one x “immutable” ie they can’t change the name associated with the phone numbers (java Strings) and Y where the name associated with the phone number can be changed. Say you write your phone number on a piece of paper and go to the center of both X and Y providers
You are just taking a copy of the phone number ( just like the copy of the reference is stored in the local variable) When you go to X the person the desk is an idiot and when you tell him that your name is now raish he does
x="ramesh"
which is exactly the same as
x= new String("ramesh")
And just writes a new number on the piece of paper and tell you the name associated with the number has been changed you go back an check on the account and your name associated with the number is still the old one, This has nothing to do with the fact that X doesn’t allow the names associated with numbers to be changed it the fault of the representative miss understanding how the piece of paper works or how to use his terminal. Which is exactly what demonstrated in 3 and 4
Now what happens at Y is simple the rep look at the database of number using functions (like in String Builder) ie using the copy or number in the analogy as a reference (x.someFunction()) which actually does change the value of the associated name (x.changeName())
Thus to change a string you should first use the reference as a reference ie using the dot operator on it and not just the “=” and in the case of String in .lang the value is immutable (ie even if you send the reference over to the function you will not be able to change it) and just like Philippe mentioned if we changed every thing to StringBuilder class it will still give the same result and won’t change the string.
In more programing terms
If( you = "don't use the reference as it mean to be")
{
// string won't be changed
}
elseif ( you = "use the reference as they are meant to be")
{
if( Objets= Immutable)
{
// String won't change
}
else if ( Object != immutable)
{
// string will change
}
}
The author is trying to explain the first if and how it has nothing to do with immutability
Perhaps, the following C program behaves the similar as the first Java code fragment above?
#include
void change(char *x) {
x = "cd";
}
int main()
{
char *x = "ab";
change(x);
printf("%sn", x);
return 0;
}
I believe what the author means is that you can change the StringBuilder reference and not change the codes behavior.
Example:
public static void main(String[] args) {
StringBuilder x = new StringBuilder("ab");
change(x);
System.out.println(x);
}
public static void change(StringBuilder x) {
x = new StringBuilder("cd");
}
This is the code if you change every instance of String to StringBuilder, remember that
x = "cd";
is the same as
x = new String("cd");
Immutability does not come into it, in both cases you are creating a new object and referencing that with your variable
If you will see java 8 Documentation, you will find this statement in String class, “Strings are constant; their values cannot be changed after they are created. String buffers support mutable strings. Because String objects are immutable they can be shared.” The statement clearly states that String is constant and immutable. That’s why you can share it between different functions without worrying about changing the value of String object. However, StringBuffer is mutable and thus the value of StringBuffer object will change if you change it in any other function. “A thread-safe, mutable sequence of characters. A string buffer is like a String, but can be modified. At any point in time it contains some particular sequence of characters, but the length and content of the sequence can be changed through certain method calls.” The explanation in 3rd and 4th are not agreeable.
In solution #5, I am wondering for a long time, what did you do to make sure, “no new object created and assigned to the parameter variable?” Thanks
“Even if String is replaced with StringBuilder, the result is still the same.” Just tried it, the result is NOT the same! With StringBuilder, the changes are carried in the same object, there’s no “new” object being created like with String, so it’s because of immutability after all!
You can modify the method like this instead of using StringBuilder which is not intuitive:
public static void main(String[] args) {
String x = new String("ab");
x = change(x);
System.out.println(x);
}
public static String change(String x) {
x = new String("cd");
return x;
}
He basically said the same thing you mentioned in your second paragraph just that he explained it using the java pass-by-value concept which makes a lot more sense.
Your suggested solution does not address the concern of not changing the reference to the original object while changing the state of the object!(i.e. We want to modify the “ab” string instance to an instance that is “cd” WITHOUT creating another “cd” string object in the string pool/heap. Hence the use of StringBuilder.) Changing x to an instance variable does nothing to prevent a new object from being created in the change() method.
That’s why using final keyword for the parameter(s) of a method is a good practice.
I don’t with you.”x stores the reference which points to the “ab” string in the heap.”is incorrect.
x stores “ab” which points to a string in the heap. “ab” itself is a reference of String object.
I`ve never thought about it as pass-by-smth case. I used to think about this as scope of visibility case. Change method has its own X variable that can take any value you like without affecting outer X variable. I have no idea how pass-by-ref(value) is related to this example.
I don’t know much about c or c++. But for java this post helped me a lot. For those of you who are still struggling to understand this concept refer to http://www.youtube.com/watch?v=W8nNdNZ40EQ. combined with this post should make things lot clear.
I do not agree on the solution. I’m not an expert of C, and may be I’m wrong on the theorical side
But the C code does not want to alter the contents of the object but the reference. So your solution (listing #3) makes no sense, since you are changing the inner state of the instance while the C want to change the referenced object
imho a simple way to explain the reason for the code listing #1 for not working (as you expected, I mean) is that x variable is defined twice, once in row 2 and then in row 7. They are called with the same local name, but the scope is different, so they are different vars, and overwriting one does not overwrite the other. A proof of that is that you can change the type of X in the method parameter (ofc you cannot then call it with a String) and the compiler would not complain at all
The solution to change X value is to have a common scope: if x is changed to be an instance variable, rather than a method scoped one, then listing #1 would produce the same effects of listing #2, even for immutable Strings (as soon as x is not marked final)