bin he
bin he

Reputation: 187

Strange behavior with string interning in Java

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 String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);

Output of the code above is:

false
true

I know that s and s2 are different objects, so the result evaluates to false, but the second result evaluates to true. Can anyone tell me the difference?

Upvotes: 15

Views: 1199

Answers (4)

Yu Jiaao
Yu Jiaao

Reputation: 4714

Just for someone who use groovy, the addition info is: the behavior is different

enter image description here

Upvotes: 0

Robby Cornelissen
Robby Cornelissen

Reputation: 97130

Here's what's happening:


Example 1

String s1 = new String("1"); 
s1.intern();
String s2 = "1";
  1. The string literal "1" (passed into the String constructor) is interned at address A.
    String s1 is created at address B because it is not a literal or constant expression.
  2. The call to intern() has no effect. String "1" is already interned, and the result of the operation is not assigned back to s1.
  3. String s2 with value "1" is retrieved from the string pool, so points to address A.

Result: Strings s1 and s2 point to different addresses.


Example 2

String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
  1. String s3 is created at address C.
  2. The call to intern() adds the string with value "11" at address C to the string pool.
  3. String 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.


Summary

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.)

Upvotes: 23

chengpohi
chengpohi

Reputation: 14217

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.

Upvotes: 12

Maurice Perry
Maurice Perry

Reputation: 9651

s.intern() doesn't change the string s. You should have written:

    s = s.intern();

Upvotes: -3

Related Questions