简体   繁体   中英

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?

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

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.

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

在此输入图像描述

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

    s = s.intern();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM