There is code as following:
String s = new String(\"1\");
s.intern();
String s2 = \"1\";
System.out.println(s == s2);
String s3 = new String(\"1\")+new
For the scenario 1:
String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s == s2);
with bytecode:
0: new #2 // class java/lang/String
3: dup
4: ldc #3 // String 1
6: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V
9: astore_1
10: aload_1
11: invokevirtual #5 // Method java/lang/String.intern:()Ljava/lang/String;
14: pop
15: ldc #3 // String 1
for String s = new String("1");
it will create a new String
object, it will have a new address with "1" that it is already in String Pool:
ldc #3 // String 1
and for s2
, as the bytecode:
15: ldc #3 // String 1
s2
is pointing to String Pool variable: "1"
, so s
and s2
have the different address and result is false
.
For the scenario 2:
String s3 = new String("1")+new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);
with bytecode:
0: new #2 // class java/lang/StringBuilder
3: dup
4: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V
7: astore_1
8: aload_1
9: ldc #4 // String 1
11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
14: ldc #4 // String 1
16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #6 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: astore_2
23: aload_2
24: invokevirtual #7 // Method java/lang/String.intern:()Ljava/lang/String;
27: astore_3
28: ldc #8 // String 11
As the bytecode, you can see new String("1")+new String("1");
is created by using StringBuilder
new #2 // class java/lang/StringBuilder
it's totally a new Object without String Pool variable.
and after s3.intern()
, this method will add current s3
to the Memory String Pool and 8: aload_1
.
and s4
is trying to load from
ldc #8 // String 11
so s3
and s4
address should equal and result is true.
s.intern() doesn't change the string s. You should have written:
s = s.intern();
Here's what's happening:
String s1 = new String("1");
s1.intern();
String s2 = "1";
"1"
(passed into the String
constructor) is interned at address A.s1
is created at address B because it is not a literal or constant expression.intern()
has no effect. String "1"
is already interned, and the result of the operation is not assigned back to s1
.s2
with value "1"
is retrieved from the string pool, so points to address A.Result: Strings s1
and s2
point to different addresses.
String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
s3
is created at address C.intern()
adds the string with value "11"
at address C to the string pool.s4
with value "11"
is retrieved from the string pool, so points to address C.Result: Strings s3
and s4
point to the same address.
String "1"
is interned before the call to intern()
is made, by virtue of its presence in the s1 = new String("1")
constructor call.
Changing that constructor call to s1 = new String(new char[]{'1'})
will make the comparison of s1 == s2
evaluate to true because both will now refer to the string that was explicitly interned by calling s1.intern()
.
(I used the code from this answer to get information about the strings' memory locations.)
Just for someone who use groovy, the addition info is: the behavior is different