Why does String.intern() return different results under JDK 8 and JDK 9?
Asked Answered
D

1

6

The following codes has different results when run using JDK 8 and JDK 9.

public static void main(String[] args) {
    String s = new String("1");
    s.intern();
    String s2 = "1";
    System.out.println(s == s2);

    String s3 = new String("1") + new String("1");
    //String s3 = "1" + "1";
    s3.intern();
    String s4 = "11";
    System.out.println(s3 == s4);
    System.out.println(s3.equals(s4));
}

under JDK 8 (version 1.8.0_172), the codes prints:

false
true
true

but using JDK 9 (version 9.0.1),the codes returns:

false
false
true

I have checked two JDK versions and they are correct. Why does the code produce different results? Is there anything wrong with my program?

Denn answered 4/11, 2018 at 13:28 Comment(3)
I checked in java-12, it shows output same as of Java-8.Genteel
When you say s3.intern(), do you actually mean s3 = s3.intern()?Mullion
Why do you care? You should normally not use intern() nor new String(String) in your code at all. While academically 'interesting', it serves no practical purpose for most ways of using Java.Doorsill
S
8

The result depends on whether the String "11" was already in the String pool prior to the call to s3.intern().

If it wasn't, s3.intern() will add s3 to the pool and return s3. In that case s4 will also be assigned the canonical representation of "11", since it was initialized with a String literal. Therefore s3==s4 would be true.

If it was, s3.intern() will return the canonical representation of "11", which is not the same instance as s3. Therefore s3==s4 would be false.

I don't have a JDK9 version to test your code on, but if that's the output you got, it implies that somewhere in the JDK9 source code that gets executed before your main, there appears the "11" String literal, which adds that String to the pool.

This is not the case in JDK8.

Your test with the "1" String gives false in both cases, since the String "1" is added to the pool when you pass it to the String constructor in new String("1"). Therefore s.intern() doesn't add the String referenced by s to the pool, and String s2 = "1"; is a difference instance than s.

The Javadoc of intern is handy when trying to understand this behavior:

String java.lang.String.intern()

Returns a canonical representation for the string object.

A pool of strings, initially empty, is maintained privately by the class String.

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.

...

All literal strings and string-valued constant expressions are interned.

Snuffy answered 4/11, 2018 at 13:38 Comment(3)
Good guess. Tested in Java 9 - when changing the string to absolute gibberish rather than "11", the result goes back to false-true-true. "11" must already be in the string pool.Unlive
@JBNizet if s3.intern() return s3 (which means "11" wasn't in the pool prior to that call), then String s4 = "11" also returns s3 (since a String literal doesn't create a new instance if an equal String is already in the pool). If "11" was already in the pool, s3.intern() will return an instance different than s3 (which would be the same instance as s4). Therefore we'll have s3 != s4Snuffy
The culprit is the field private static final java.lang.String VERSION_BUILD = "11"; in java.lang.VersionProps. Which applies to the JDK 9.0.1 named in the question, as its full version is 9.0.1+11 and also to the version 9.0.4+11.Slantwise

© 2022 - 2024 — McMap. All rights reserved.