From my understanding of Java, String is immutable. So the following will not concatenate and only give me the output app, proving it is immutable. I get that.
String apple = new String("app");
apple.concat("le");
System.out.println(apple);
But I am able to change the value of the String after initializing it with some value. Isn't this considered immutable anyway?
String orange = "ora";
orange = "orange";
System.out.println(orange);
The Object is immutable, the reference is not. You can change the reference as often as you like with orange = "Orange" but you are not changing the object. You are merely letting go of the old one and attaching the reference to the new one.
When you do:
orange = "orange";
what happens behind the scenes is:
orange = new String("orange");
That is, orange, as a reference to a String, is modified. It just points to a different String instance.
In this statement:
apple.concat("le");
what happens is that you create a new String object which is the result of apple.concat(new String("le")), but you don't assign it...
But then again you are dealing with strings here; strings are one of the few classes in Java which have built-in behaviors, such as:
final String x = "a" + "b";
which in fact translates to:
final String x = new StringBuilder("a").append("b").toString();
You have stepped into a hornet's nest.
Instances of the String class are immutable. The apple variable is not immutable, but it would be if you declared it final.
For example, the following code is mutating the object:
Person p = new Person("John");
p.setName("Jenny"); // <-- mutating
Immutable objects (like String, Integer, ...) have no method that change the state of the object.
In contrast, the following code is changing the value of p, but is not mutating any objects:
Person p = new Person("John");
p = new Person("Jenny");
p is simply updated to reference a different Person object. The original "John" object still exists (until garbage collected).
Now, if p was defined as final, then it (the variable) would be immutable too:
final Person p = new Person("John");
// Cannot: p = new Person("Jenny");
In your particular case, concat does not mutate the String, it creates a new string and returns it, so you need to:
String apple = new String("app");
apple = apple.concat("le");
System.out.println(apple); // prints "apple"
Here the variable is updated, but not the String object.
According to Wikipedia, an immutable object is:
an object whose state cannot be modified after it is created.
However, you are not modifying the original orange String object ("ora"): you are creating a new String object ("orange"), and this new object is being assigned to a variable.
Easy as cake
If that doesn't make sense, think of it this way. Supposing some Object was a block of chocolate cake, you can add icing or cut out parts of this cake without actually changing the identity of the cake. However, if you wanted to change it to a red velvet cake, you have to make a new cake, because you cannot modify a chocolate cake to make it into a red velvet cake (for practical purposes) the original. An immutable object means you cannot cut, or ice... that "cake", but you can replace it with a new cake.
orange = "orange" only means that orange is pointing to a new String instance instead of the old one ("ora").
Immutability means that you cannot change an object's state once it has been created. You haven't changed the state of the string instance that orange is referring to; you just made it refer to a new instance. Changing an object's state requires you to either call a method on that object that changes its state, or modify one of its publicly-accessible properties.
You're also confusing immutability of objects with immutability of references. To make a reference immutable (i.e., make sure you cannot make it point to something else once it has been initialized) you use the final keyword.
So if you had:
final String orange = "ora";
orange = "orange";
You would now get an error because the reference is immutable and cannot be reassigned.
The Object is immutable while the reference is not. You can change the reference but not object.
Adding an example to make us more clear
String orange1 = "ora";
String orange2 = orange1;
System.out.println("orange1: " + orange1 + " , orange2: " + orange2);
System.out.println("----------------------");
orange2 = "orange";
System.out.println("orange1: " + orange1 + " , orange2: " + orange2);
Related
This question already has answers here:
Does Java return by reference or by value
(4 answers)
Closed 4 years ago.
I am quite confused about everything being a reference in most cases but sometimes not, and containers. Consider this piece of code:
HashMap<Integer,String> x_map = new HashMap<>();
x_map.put(42,"foo");
String g = x_map.get(42);
g = "bar";
System.out.println(g.equalsIgnoreCase(x_map.get(42)));
Why does this print false ?
I found the correct way to do it:
How to update a value, given a key in a java hashmap?, but I am totaly lost why the above does not work as expected. Also when the objects in the map are rather large then I would like to be able to modify only a single member of one of the values in the map and currently I dont know how to do that without creating a new instance and put that into the map.
Pass-by-value
Java is pass-by-value (see Is Java “pass-by-reference” or “pass-by-value”?).
Which means that when you do
Person first = new Person("John");
Person second = first;
you have two different variables, both pointing to the instance new Person("John") in memory. But again, both variables are different:
first ---|
|---> new Person("John")
second ---|
If you manipulate the object itself the change is reflected to both variables:
second.setName("Jane");
System.out.println(first.getName()); // Jane
The diagram is now:
first ---|
|---> new Person("John").setName("Jane")
second ---|
But if you change where a variable points to, it obviously does not affect the other variable since you didn't touch the object they point to:
second = new Person("Jane");
System.out.println(first.getName()); // John
After that statement you have two variables pointing to different objects:
first ---> new Person("John")
second ---> new Person("Jane")
Explanation
Let's transfer that knowledge to your code. You wrote:
String g = x_map.get(42);
g = "bar";
and are asking why g = "bar" did not affect what the map stores.
As said, you only change where the variable g is pointing to. You don't manipulate the object inside the map. The diagram is first:
|---> x_map.get(42)
g ---|
"bar"
and then
x_map.get(42)
g ---|
|---> "bar"
Solution
If you want to change the object, you would need to manipulate it directly instead of only changing the variable reference. Like first.setName("Jane"). However, in Java Strings are immutable. Meaning that there is no way of manipulating a String. Methods like String#replace don't manipulate the original string, they always create a new String object and leave the old one unchanged.
So in order to change what the map stores for they key 42 you just use the Map#put method:
x_map.put(42, "bar");
This will change what x_map stores for the key 42 to "bar".
Because you didn't update the value of the 42 key in the x_map.
for that you should do this
X_map.put(42, g)
The reason is given in a comment by #khelwood:
Variables are assigned by value in Java. g = x_map.get(42) just
assigns the value from the map into the variable g. If you reassign g,
then you're repointing g to a different value.
If instead of String I put something that actually is modifiable in the map then it works just expected:
public static class Test{
public int x = 5;
}
and then...
HashMap<Integer,Test> x_map = new HashMap<>();
x_map.put(42,new Test());
Test g = x_map.get(42);
g.x = 12;
System.out.println((g.x == x_map.get(42).x) + " " + g.x + " " + x_map.get(42).x);
prints true 12 12.
Is it correct to say that immutable objects are created by deep copy everytime we want to change/reference original object, while mutable objects are created by shallow copy when we create another reference.
For example:
String s1 = "s";
String s2 = s1;
and:
Person p1 = new Person();
Person p2 = p1;
As far as I'm concerned, s2 will get it own copy of "s" (not a copy of reference).
How this fit the String pool concept with single instances of String literals?
In case of Person we will have two references to one Person instance right?
Immutable objects cannot be changed. Their state remains constant after creation. Thus, "s" is constant. But the references that point to it could change.
String s1 = "s";
String s2 = s1;
Here, "s" is an immutable object (String), s1 and s2are just references.
Defensive copies are used to prevent state changes on an Object - part of the immutability story , by returning a new Object with the same values.
PS : You are not doing deep copying here. You are just re-assigning references.
This question already has answers here:
Is Java "pass-by-reference" or "pass-by-value"?
(93 answers)
Closed 6 years ago.
I have below two situations related to ArrayList get method, one with custom class and one with String class:
1. Below is the example of modifying Custom class ArrayList element:
ArrayList<MyClass> mTmpArray1 = new ArrayList<MyClass>();
MyClass myObj1 = new MyClass(10);
mTmpArray1.add(myObj1);
MyClass myObj2 = mTmpArray1.get(0);
myObj2.myInt = 20;
MyClass myObj3 = mTmpArray1.get(0);
Log.d(TAG, "Int Value:"+myObj3.myInt); // Prints "20"
2. And below is the example of modifying String ArrayList element:
ArrayList<String> mTmpArray2 = new ArrayList<String>();
mTmpArray2.add("Test_10");
String myStr1 = mTmpArray2.get(0);
myStr1 = "Test_20";
String myStr2 = mTmpArray2.get(0);
Log.d(TAG, "Str Value:"+myStr2); // Prints "Test_10"
So in case of MyClass ArrayList, when I call get and modify the value, then I see change is reflecting when I do get again.
But same way when I modify String ArrayList, then changes are not reflecting.
What is the different in of the get method in both the scenarios?
Is it that in case of String, String class creating deep copy and returns new object, and in case of Custom class shallow copy is created?
In the first scenario applicable to "LinkedHashMap", "HashMap" and "List"?
Your are not doing the same thing in the two cases.
Here you update the state of an object, so the change affects the object stored in the list :
myObj2.myInt = 20;
Here you are assigning a new object to a local variable, so the list is not affected :
myStr1 = "Test_20";
If String was mutable, you could have modified the String by calling some method, and the change would have been reflected in the object stored in the list :
myStr1.setSomething(...);
On the other hand, if in the first case you would have changed the value of the local variable, the object stored in the list wouldn't have been affected :
myObj2 = new MyClass (...);
Strings are immutable. You're not inserting the new string into the array list.
When you do String myStr2 = mTmpArray2.get(0);, even tho you are pointing to a reference in the ArrayList, any attempt to change the value, will (because of String immutability) create a new String (myStr2) that will not reference the ArrayList anymore.
When you do myStr1 = "xxx", you're not actually changing the ArrayList reference, you're changing a new (copy) (now called myStr1) that was grabbed from the ArrayList and it has a local scope.
Read some more about Strings: Immutability of Strings in Java
Now in the first example, you are pointing to a mutable object (your custom class) so you're literally changing the direct value, through the reference. Welcome to Java. ;)
Unrelated: This code: MyClass myObj1 = new MyClass(10); is (arguably) considered bad. It's better to use a factory pattern that is a lot easier to read. In other words, public constructors with parameters are hard to read (for example, I have no idea what I am constructing when I read your code).
A (perhaps) better approach would be: MyClass myObj = MyClass.createInstanceWithCapacity(10); // i've invented the name because I don't know what your 10 is, but look at both, which one do you think is easier to understand upon first glance?
Disclaimer: The above unrelated comment is my personal opinion and not all developers will agree. ;)
Strings have very nice property called "Immutablity"
This means that String cannot be mutable (changed), when we create/
try to refer to old string, a new instance string is created. And any
changes we do are saved in new instance and it do not affect the old
string
For example,
String s = "Old String";
System.out.println("Old String : "+s); // output : Old String
String s2 = s;
s2 = s2.concat(" made New");
System.out.println("New String : "+s2); // output : Old String made New
System.out.println("Old String is not changed : "+s); // output : Old String
These is no difference between the two "get" calls. The difference is between the types that the ArrayList is holding, and what you're doing the references the "get" method returns.
In your first example, you do this:
MyClass myObj2 = mTmpArray1.get(0);
myObj2.myInt = 20;
Here, you're getting a reference to the MyClass instance in the ArrayList in position 0, and you are modifying a field within this instance.
In your second example, you do this:
String myStr1 = mTmpArray2.get(0);
myStr1 = "Test_20";
Here, you're getting a reference to the String instance in the array list, and then you're giving myStr1 a reference to a different string which you create ("Test_20"). It's as if you did wrote myObj2 = new MyClass(20); in the 2nd line in the 1st example.
So, in short, in the 1st example, you access a field within the object by altering it from the reference you grabbed. In the 2nd example, you simply altered your reference to point at a different String.
I should also mention that in Java, Strings are immutable, meaning once they have been created, they cannot be changed.
String is an immutable class. A line like
myStr1 = "Test_20";
does not change the value of the String object myStr1 is pointing to. Instead a new String is created and myStr1 is modified to point to the new String. The original String is unchanged and can be retrieved from the ArrayList.
Your MyClass object is clearly mutable. Only one instance is created and its state is changed by the assignment
myObj2.myInt = 20;
Hence when this object is retrieved from the ArrayList, its new state is seen.
You simply do NOT change the list in your 2nd example.
In the first example, you are doing this:
Get the first object from the list and store it in the variable called 'myObj2'
Modify the object stored in variable 'myObj2' by setting the int value of this object
But your second example is completely different:
String myStr1 = mTmpArray2.get(0);
myStr1 = "Test_20";
Let me translate that:
Get the first element from the list and store it in the variable called 'myStr1'.
Set the value of the variable 'myStr1' to "Test_20"
So, in case one you are modifying a variable of the object stored in the list. In case two you are retrieving the object stored in the list - and then re-use the variable you stored that retrieved object in and use it for something new - but that does not change the original list, of course.
To modify your list for a type like string, you would need to use set(x, "Test_20").
This question already has answers here:
What is meant by immutable?
(17 answers)
Closed 9 years ago.
I'm trying to understand exactly how Java strings are immutable. I get that this should probably be an easy concept, but after reading several online web pages I still don't quite understand.
I don't understand how Java Strings are "immutable". I currently have the following code:
public static void main(String[] args) {
String name = "Jacob Perkins";
System.out.println( name );
name = name + "!";
System.out.println( name );
}
My output is the following:
Jacob Perkins
Jacob Perkins!
Why is this happening if a string is supposed to be immutable? Why am I able to re-assign a value to the string?
Let an image explain this for you:
On the left side, you have the variable, which in fact is a reference.
String name = "Jacob Perkins;" The String "Jacob Perkins" is created, and name points to it.
name = name + "!"; A new String "Jakob Perkins!" is created, and the reference now points to the new String. The old one, however, remains unchanged, because String is immutable.
The string itself, once created, can never be changed. What your code sample does is to replace the string in name with a new string that was constructed from the previous contents of name, and an exclamation point. (The original contents of name, which are no longer referenced by any variable, will eventually be reaped by the garbage collector.)
If you were to examine the compiled code (or step through it with a debugger), you would discover that your name + "!" expression had been compiled into the creation of a StringBuilder object and a few operations on that object.
That is, the strings are immutable, but the variable name is not. Its value changes, to point to different strings. The strings themselves never change.
The String objects are immutable only the variable reference changes.
In your example the "Jacob Perkins" object still exists and a new object "Jacob Perkins!" gets created.
The name variable points to the new object.
Maybe this will help you to understand.
Let's say you have a class Point (java.awt.Point).
Let's say you have one instance p:
Point p = new Point(0,0);
Then you make create a new variable y referencing the same object p as:
Point y = p;
If you change the value of p, you also change y. Because the class Point is mutable.
p.setLocation(1,1);
It makes y reference the location 1,1 as well.
Using the String class it does not happen.
String a = "123";
String b = a;
If you make a = a + "4";
The new value of a will be "1234" but b is still "123";
The object they were referencing didn't change, it's just that a is pointing to another object now.
What actually happens is there are 3 String objects created. "Jacob Perkins", "!" and "Jacob Perkins!". You didn't really modify the "Jacob Perkins" instance. You just changed the reference of the name variable, from "Jacob Perkins" instance to "Jacob Perkins!".
String name = "Jacob Perkins";
String name2 = name + "!";
name.substring(5); // or wather syntax is
Those do not change variable name
In Java there are references and there are values.
When you say
String foo = "John Smith";
foo is a variable, holding a reference. The reference points to the object, or value, containing "John Smith".
The reference held by the variable is mutable. I can do this:
foo = "Jack Smith";
And I have mutated the reference. However if I write code like this:
String foo = "John Smith";
String bar = foo; //bar's reference is a copy of foo's reference - the same value
foo = "Jack Smith";
System.out.println(bar); //prints John Smith
We see that even though we changed foo, bar has not changed. Why? Simply, when we wrote foo = "Jack Smith"; we made foo point to a new String. We didn't reach through foo and modify the string, since No method in Java mutates, or can mutate a String. Instead, a new String is returned, and the variable is made to point to the new, also immutable String. This way, the object a String variable 'points' to can never be modified by anything but through that variable. This is an important property of the Java language that it guarantees.
What is difference between in the following statements
String name = "Tiger";
final String name ="Tiger";
Although the String class is final class, why do we need to create a String "CONSTANT" variable as final?
final in this context means that the variable name can only be assigned once. Assigning a different String object to it again results in a compile error.
I think the source of the confusion here is that the final keyword can be used in several different contexts:
final class: The class cannot be subclassed.
final method: The method cannot be overridden.
final variable: The variable can only be assigned once.
See the Wikipedia article on final in Java for examples on each case.
"final" means different things in the two cases.
The java.lang.String class is final. This means you can't inherit from it.
The variable "name" is final, meaning that you can't change it to point to a different instance of String. So a non-final String variable isn't a constant, because you could read it at two different times and get different values.
As it happens, Java string objects are also immutable. This means that you cannot modify the value which a particular String object represents. Compare this with an array - you can replace the first element of an array object with a different object, but you can't replace the first character of a String object with a different char. This is why String.replace() returns a new string - it can't modify the old one.
One reason that String is final is to prevent an instance of a subclass of String, which implements mutable behaviour, being passed in place of a String.
But whether you can modify a particular object, and whether you can assign a different object to a variable, are completely different concepts. One is a property of String objects, and the other is a property of String variables, which are references to String objects.
Remember that Java final keyword serves two purposes in this case:
it means the reference cannot be set to another String-- i.e. you cannot subsequently do "name = ...";
but crucially, it means that the reference is correctly published to other threads (see linked article for more details, or works such as Goetz et al, "Java Concurrency in Practice".
You are confusing immutable with final.
String, like Integer and Long, is an immutable class in that the internal data is protected from modification through encapsulation.
However, like Ayman said, final refers to the pointer to the string.
Have a look at The final word on the final keyword.
String name = "scott";
name = "tiger"; // OK
final String gender = "male";
gender = "female"; // won't compile you cannot reassign gender cause it's final
If a variable is marked as final then the value of that variable cannot be changed i.e final keyword when used with a variable makes it a constant. And if you try to change the value of that variable during the course of your program the compiler will give you an error.
NOTE :
If you mark variable of a reference type as final, that variable cannot refer to any other object. However, you can change the object's contents, because only the reference itself is final.
SOURCE : Final Keyword in Java
To deduce that String objects are Final by default is in itself a vague statement. The basics of Java dictate that if an instance variable is not pointing to a memory location it becomes eligible for Garbage collection. The same thing happens with the String objects. They are immutable but their references can be changed. To overcome this we can use "Final String s1 = "Final String" " the final keyword won't allow any assignment to s1 except at the time of First Declaration, making it truly immutable.
public class DemoStringF
{
String s1; //Declaring an Instance Variable to type String.
public static void main(String... args)
{
DemoStringF d = new DemoStringF ();
d.s1 = "Intializing s1 here"; //Initializing the s1
System.out.println("Value ref. by s1 is " +d.s1); //Displays the String
by which s1 is
initialized.
System.out.println("Value of s1 is " +d.s1.hashCode()); //Displays the
value of the s1.
d.s1 = d.s1.concat(" Adding String to s1"); //Changing the value ref. by
s1.
System.out.println("Value ref. by s1 after concat() is " +d.s1);
//Displays a new value of s1.
System.out.println("Value of s1 is " +d.s1.hashCode()); //Displays
the value of the s1.
}
}