Why does new String() + new String() return different results under JDK8

Viewed 107

The following codes has different results under JDK8 1.8.0_171

@Test
public void test2() {
    String s1 = new String("a") + new String("a");
    s1.intern();
    String s2 = "aa";
    System.out.println(s1 == s2); //true
}
@Test
public void test3() {
    String s1 = new String("1") + new String("1");
    s1.intern();
    String s2 = "11";
    System.out.println(s1 == s2); //false
}

The only difference is the value: "a" instead of "1", the result I get is different. Why is this?

3 Answers

s1.intern() only adds the String referenced by s1 to the String pool if the pool doesn't already contain a String equal to it.

String literals such as "aa" and "11" are always interned, so they will always be == the instance returned by s1.intern().

Therefore, whether or not s1 == s2 returns true depends on whether or not the String pool contained a String equals to s1 before s1.intern() was called.

  • If it did, s1.intern() which is == s2 is not == s1, so System.out.println(s1 == s2) will print false.
  • If it did not, s1.intern () == s1 == s2, so System.out.println(s1 == s2) will print true.

This can change between Java versions, since JDK classes of different versions may contain a different set of String literals, which are automatically interned before your code is executed.

This is to add an illustration to Eran's great answer. The following assumes "aa" is not interned prior to the execution of the code (implicitly or not):

String s1 = new String("a") + new String("a");
s1.intern();
String s2 = "aa";
System.out.println(s1 == s2); //true

String s3 = new String("a") + new String("a"); //same text
s3.intern();
System.out.println(s3 == s2); //false

Both s1 and s3 have the same text ("aa"), so the difference in actual text doesn't make the difference. The second call to .intern() simply didn't result in s3 being put in the pool (because the pool already contained the text "aa", which was the result of s1.intern()), explaining why == returns false between s3 and s2.

In short, it means that "11" was interned prior to the execution of your test3() method.

And for an additional test, move String s2 = "aa"; to the beginning of the code (both comparisons return false, for the same reason - although this may be version or implementation-dependant)

Because the == operator checks if both variables reference the exact same object. For example

String s1 = new String("foo");
String s2 = s1;
System.out.println(s1 == s2); //true

If you have two separate instances and you want to compare Strings based on their values you should be using String::equals.

String s1 = new String("foo");
String s2 = new String("foo");
System.out.println(s1.equals(s2)); //true

Please check out How do I compare strings in Java?

When it comes to String:intern you'd have to call it on both instances in order to compare them, not just one of them. String foo = "foo"; still implicitly creates a new instance of a String object. There is no primitive string in Java.

String s1 = new String("foo");
String s2 = "foo";
System.out.println(s1.intern() == s2.intern());
Related