String is Immutable (Fact)
Lets say I have two objects of string given below:
String str1 = new String("dave");
String str2 = new String("dave");
While compiling, Interning of string objects happens. And according to Wikipedia, It says "string interning is a method of storing only one copy of each distinct string value, which must be immutable. All compile-time constant strings in Java are automatically interned using this method."
If that's a case, then what would happen to my two references str1 and str2 ? To which object they will point out once compiled ?
I hope its valid question, otherwise correct me, I'm confused little bit.
Below both str1 and str2 are created on heap, because you are creating them at run time. Even both objects are created at different references.
String str1 = new String("dave");
String str2 = new String("dave");
Even str1 == str2 will return false. Their values are same but they are two different objects on heap.
However if you create the object at compile time, as example
String str1="dave";
String str2="dave";
then str1 == str2 will return true, because both these objects are
part of same reference created at compile time in string pool.
When we use double quotes to create a String, it first looks for String with same value in the String pool, if found it just returns the reference else it creates a new String in the pool and then returns the reference.
Related
This question already has answers here:
a confusion about java String literal pool and String's concatenation
(4 answers)
When should we use intern method of String on String literals
(14 answers)
Closed 6 years ago.
Here is my code and I am now quite confuse about String pool and
Heap storage by this output.
public class String1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
String str = "abcd";
String str1 = "" ;
str1=str1+"abcd";
if(str.equals(str1))
System.out.println("True");
else
System.out.println("False");
if(str == str1)
System.out.println("True");
else
System.out.println("False");
}
}
Now, I am creating String str and will be stored in string pool (Correct me if I am getting wrong!).
Now after concat str1 with string "abcd" they both have same value.
So, I think str and str1 should have same reference in String pool and So, 2nd if statement should print true but it prints false.
So, my question why str and str1 not getting same reference ?
Java automatically interns (means, puts them into the String pool) String literals, not newly created Strings. See also https://stackoverflow.com/a/1855183/1611055.
Remember that Strings are immutable, so the + operator must create a new String - it can not append to the existing one. Internally, the + operator uses a StringBuilder to concatenate the strings. The final result is retrieved through StringBuilder.toString() which essentially does return new String(value, 0, count);.
This newly created String is not automatically put into the String pool.
Hence the str1 reference is different from str even though the strings have the same content. str points to a location in the string pool, while str1 points to a location on the heap.
If you add
str1 = str1.intern();
after str1 = str1 + "abcd"; to explicitly intern the newly created String, your second if statement returns true.
Alternatively, str1 = (str1 + "abcd").intern(); would have the same effect.
In case of strings to compare the values we should use equals method as it compares values that are present in the string variables.
But when we choose to compare string variables using == it compares the addresses of the String object not the values so it will return false even if they have same values in it.
you are right that the strings get added to the string pool. but == checks if both the objects are pointing to the same reference (to make it simpler pointing to the same memory location) in the string pool or not. whereas .equals() method check if the value of the both the object are same or not.
public static void main(String[] args) {
String str1 = new StringBuilder("计算机").append("软件").toString();
System.out.println(str1.intern() == str1);
String str2 = new StringBuffer("ja").append("va").toString();
System.out.println(str2.intern() == str2);
}
Results:
true
false
First one prints true, and the second prints false. Why are the results different?
The difference in behavior is unrelated to the differences between StringBuilder and StringBuffer.
The javadoc of String#intern() states that it returns
When the intern method is invoked, if the pool already contains a
string equal to this String object as determined by the equals(Object)
method, then the string from the pool is returned. Otherwise, this
String object is added to the pool and a reference to this String
object is returned.
The String created from
String str2 = new StringBuffer("ja").append("va").toString();
is a brand new String that does not belong to the pool.
For
str2.intern() == str2
to return false, the intern() call must have returned a different reference value, ie. the String "java" was already in the pool.
In the first comparison, the String "计算机软件" was not in the string pool prior to the call to intern(). intern() therefore returned the same reference as the one stored in str2. The reference equality str2 == str2 therefore returns true.
Because your assignments don't re-read from the intern pool and Java String(s) are immutable. Consider
String str1 = new StringBuilder("计算机").append("软件").toString();
String str1a = new String(str1); // <-- refers to a different String
str1 = str1.intern();
str1a = str1a.intern();
System.out.println(str1a == str1);
String str2 = new StringBuffer("ja").append("va").toString();
String str2a = new String(str2); // <-- refers to a different String
str2 = str2.intern();
str2a = str2a.intern();
System.out.println(str2a == str2);
The output is (as you might expect)
true
true
Lots of answer before mentioned about the pool and explained really clearly with the Oracle link docs.
I just would like to point out the way we can check when debugging code.
String str1 = new StringBuilder("计算机").append("软件").toString();
System.out.println(str1.intern() == str1);//the str1.intern() returns the same memory address the str1
String str2 = new StringBuffer("ja").append("va").toString();
System.out.println(str2.intern() == str2);//the str2.intern() does not return the same memory address the str2
You can use any IDE and debug to check the actual address that the str1 and str1.intern()/str2 and str2.intern().
let me add something more interesting:
the OpenJDk 8 is true,true;
Oracle JDK 6 is true,true;
so I think the right answer is :
diffrent vendor's jvm or jvm versions may have diffrent implemention(the language specification don't force the how to)
In Oracle JDK 8(I guess u using):
String “java” already in pool(loaded by java.lang.Version#laucher_name) and String pool only stores the refrence,not the object.
But in OpenJDK the laucher_name is "openJDK";In Oracle JDK 6 and below ,the string pool will copy the string object to itslef.
I recently learned that in Java: == compares the object references, not the content, which is why:
String str1 = "hell";
String str2 = "o";
String str3 = str1 + str2;
String str4 = "hello";
str3 == str4; // False
So far so good. However when I do the following:
String str5 = "hello";
str5 == str4; // True
Does this mean that str5 and str4 reference the same memory object? How does this work?
The String str5 = "hello"; creates a pooled String value hello, which is why str5 == str4 returns true.
On the other hand, str1 + str2 works like this:
An instance of the StringBuilder class is created (behind the scenes)
The + operator actually invokes the StringBuilder#append(String s) method
When the appending is done, a StringBuilder.toString() method is invoked, which returns a brand new String object. This is why str3 == str4 is actually false.
More info:
How do I compare Strings in Java?
How Java do the string concatenation using “+”?
Yes. str5 and str4 refer the same memory object. As Strings are immutable when you change the value of some string its produce an different object. If two String objects have the same value then second one is not created, JVM just give the reference of the first object.
When the value of String changed different object created for some security and other usefull purpose read these link:
Immutability of Strings
wiki Immutable object
When some string are created like
String str1="hello";
JVM creates an immutable object when again you try to create some string with same value
String str2="hello"
JVM use the same procedure to create an object as see's that this object is already created then its return the object of the str1 to reduce duplicate object creation.
This will be useful string pool in the jvm
Yes, when you create and assign a String value eg String s1="hello"; , it gets added in the String pool. Now if you assign the same String value to another reference like this:-
String s2="hello";
The variable s2 will point to the same String object hello , present in String pool.
However you can force and create a new String object for the same values like this:-
String s3= new String("hello");
This will add and create new object for hello even though it is already present in the String pool.
Hence it can be summarised as:-
s1==s2; //return true
s1==s3; //return false
s2==s3; //returns false
Strings are pooled by the JVM once they are created, that's why those variables refer to the same instance in the String pool.
And the idea behind having a String pool is to avoid unnecessary object creation.
Strings are immutable objects. This is why str1 and str2 combined are not equal to str3.
Ya,its gives true for str5 ==str4 becoz it uses"string pool area" to store."this time also it
compares object references" but these strings has same object reference as string pool area has one object id.
In first case strings are not created in string pool area thats why it gives false.
A private pool of string literals is maintained by String class. All literal strings and string-valued constant expressions are added to this pool during program start up. So all string literals with same value will point to same object.
When a new string object is created(e.g. by concatenating two string objects) it does not belong to the String pool, so comparing it with another string literal will return false. Any string object can be added to String pool by invoking intern() method on that string object, now the object will point to a string literal from the pool which have same value as this object. Now any string comparison of this object with same string literal will yield true.
I know there are two ways of creating String in Java:
String a = "aaa";
String b = new String("bbb");
With the first way Java will definitely create a String object in the string pool and make a refer to it. (Assume "aaa" wan't in the pool before.)
With the second method, an object will be created in the heap, but will jvm also create an object in the string pool?
In this post Questions about Java's String pool, #Jesper said:
If you do this:
String s = new String("abc");
then there will be one String object in the pool, the one that represents the literal "abc", > and there will be a separate String object, not in the pool, that contains a copy of the > content of the pooled object.
If that's true, then every time with the new String("bbb");, a object "bbb" is created in the pool, which means by either way above, java will always create a string object in the pool. Then what is intern() used for ? In the docs http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#intern(), it says:
When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.
That means there are cases that a string is not in the pool, is that possible ? Which one is true ?
As you know that String is an immutable object in Java programming language, which means once constructed can not be altered. Due to this, JVM has the ability to maintain a literal pool which is helpful to reduce the memory usage and to increase the performance. Each time when a String literal is used JVM checks the literal pool. If the literal is already available, the same reference would be returned. If the literal is not available, a new String object will be created and added in the literal pool.
This theory is applied when you try to create a String like a primitive or a literal/constant.
String str = "bbb";
But when you create a new String object
String str = new String("bbb");
the above mentioned rules are overridden and a new instance is created always.
But the intern API in the String class can be used to pick the String reference from the literal pool even though you create a String using new operator. Please check the below given example. Although the str3 is created using new operator since we used the intern method JVM picked up the reference from the literal pool.
public class StringInternExample {
public static void main(final String args[]) {
final String str = "bbb";
final String str1 = "bbb";
final String str2 = new String("bbb");
final String str3 = new String("bbb").intern();
System.out.println("str == str1 : "+(str == str1));
System.out.println("str == str2 : "+(str == str2));
System.out.println("str == str3 : "+(str == str3));
}
}
Output of above code:
str == str1 : true
str == str2 : false
str == str3 : true
You can have a look: Confusion on string immutability
Source of answer: http://ourownjava.com/java/java-string-immutability-and-intern-method/
Shishir
There are essentially two ways that our String objects can enter in to the pool:
Using a literal in source code like "bbb".
Using intern.
intern is for when you have a String that's not otherwise from the pool. For example:
String bb = "bbb".substring(1); // substring creates a new object
System.out.println(bb == "bb"); // false
System.out.println(bb.intern() == "bb"); // true
Or slightly different:
System.out.println(new String("bbb").intern() == "bbb"); // true
new String("bbb") does create two objects...
String fromLiteral = "bbb"; // in pool
String fromNewString = new String(fromLiteral); // not in pool
...but it's more like a special case. It creates two objects because "bbb" refers to an object:
A string literal is a reference to an instance of class String [...].
Moreover, a string literal always refers to the same instance of class String.
And new String(...) creates a copy of it.
However, there are many ways String objects are created without using a literal, such as:
All the String methods that perform some kind of mutation. (substring, split, replace, etc.)
Reading a String from some kind of input such as a Scanner or Reader.
Concatenation when at least one operand is not a compile-time constant.
intern lets you add them to the pool or retrieve an existing object if there was one. Under most circumstances interning Strings is unnecessary but it can be used as an optimization because:
It lets you compare with ==.
It can save memory because duplicates can be garbage collected.
Yes, new String("abc") will create a new object in memory, and thus it is advised to avoid it. Please have a look at item 5 of Josh Bloch's Effective Java, "Avoid creating unnecessary objects" where it is better explained:
As an extreme example of what not to do, consider this statement:
String s = new String("stringette"); // DON'T DO THIS!
The statement
creates a new String instance each time it is executed, and none of
those object creations is necessary. The argument to the String
constructor ("stringette") is itself a String instance, functionally
identical to all of the objects created by the constructor. If this
usage occurs in a loop or in a frequently invoked method, millions of
String instances can be created needlessly. The improved version is
simply the following:
String s = "stringette";
This version uses a
single String instance, rather than creating a new one each time it is
executed. Furthermore, it is guaranteed that the object will be reused
by any other code running in the same virtual machine that happens to
contain the same string literal [JLS, 3.10.5].
http://uet.vnu.edu.vn/~chauttm/e-books/java/Effective.Java.2nd.Edition.May.2008.3000th.Release.pdf
With the second method, an object will be created in the heap, but will jvm also create an object in the string pool?
Yes, but it is the string literal "bbb" which ensures the interned string1. The string constructor creates a new string object which is a copy with the same length and content - the newly created string is not automatically interned.
If that's true, then every time with the new String("bbb");, a object "bbb" is created in the pool, which means by either way above, java will always create a string object in the pool. Then what is intern() used for ?
Only string literals are automatically interned. Other string objects must be manually interned, if such is the desired behavior.
That means there are cases that a string is not in the pool, is that possible ?
With the exception of manual calls to String.intern, only string literals result in interned strings.
While I would recommend using a specialized collection for such cases, interning may be useful where it can be used to avoid creating extra duplicate objects. Some use-cases where interning can be beneficial - as in, the same string value can appear many times - is in JSON keys and XML element/attribute names.
1 This is trivial to reason, consider:
String _b = "bbb"; // string from string literal (this is interned)
String b = new String(_b); // create a NEW string via "copy constructor"
b == _b // -> false (new did NOT return an interned string)
b.equals(_b) // -> true (but it did return an equivalent string)
b.intern() == _b // -> true (which interns to .. the same string object)
Consider statement:
String s=new String("abc");
Will this statement creates two String objects namely "abc" and the one represented by 's'?
and if it creates two objects then will "abc" get stored in String pool or just discarded?
EDIT:
i am asking this question in reference to Difference between string object and string literal, where in the last two answers , creation of two objects is denied.
Avoid such kind of behavior , because "abc" is already a String and by making a new String, you are creating an unnecessary Object.
Instead go for String s = "abc";
This way, the String gets interned by the JVM and is added to a pool.
To answer your question, you are just creating an Object s that is referring to "abc".
So when you do say String t = new String("abc"); and then do s==t, will yield in false. Because they have their separate instances to abc.
String s = "HELLO";
Here "s" is a object reference variable of type String, which refers to the String literal object "Hello" which is added to the String Literal Pool.
String t = new String("Hello");
Here t is a object reference variable of type String, which refers to the String object "Hello" which is added to the String Pool.
Difference Between String Literal and String :
Assume
String s = "Hello";
String t = new String("Hello");
Now if following changes are done:
s = null;
t = null;
Hello String object associated with t will be a candidate for Garbage Collector, But Hello String Literal associated with s will NOT BE A CANDIDATE for Garbage Collector, as there will ALWAYS BE A REFERENCE FROM STRING LITERAL POOL to it.