Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problem of Java String intern method when using StringBuilder with append method creating a String

Tags:

java

string

    public static void main(String[] args) {
        String a = new StringBuilder("Jav").toString();
        a.intern();
        String c = "Jav";
        System.out.println(a == c);  //false
    }
    public static void main(String[] args) {
        String a = new StringBuilder("Ja").append("v").toString();
        a.intern();
        String c = "Jav";
        System.out.println(a == c);  //true
    }

enter image description here

I do not understand why first one would print out false, but the second one print out true. I think those should print out true because after calling the intern method, the reference of a would be written into the string pool and c will point to the reference of a. Can someone explain the difference? Thanks!

My JDK Version: OpenJDK 17.0.7

Btw I also get the same result when using StringBuffer.

like image 931
Mattyeh Avatar asked Jul 15 '26 23:07

Mattyeh


2 Answers

TL;DR and solution

The important difference is the location of the first "Jav" literal/interning.

intern() returns you the canonical reference. If a canonical reference already exists, it is not updated.

Hence, you need to change a.intern(); to a = a.intern();:

public static void main(String[] args) {
    String a = new StringBuilder("Jav").toString();
    a = a.intern();
    String c = "Jav";
    System.out.println(a == c); //this is true now
}

Why and how the examples are different

In your second example, == returns true because the String returned by StringBuilder is made the canonical reference in the first intern() call and the String literal is resolved lazily and gives.

The literal then gives you the canonical reference which is the same object you already have.

In the first example, a canonical String already exists with that content hence the intern() method just returns the existing canonical String as opposed to making the current String canonical.

Further comments

For a more detailed analysis of your code, see @Nidheesh R's answer.

As mentioned in the comments by @Jorn, you shouldn't need String#intern anyways and you should also not use == on Strings.

Also, depending on that behaviour is fragile. If you are using tools like GraalVM's native-image, using a different JVM or maybe some of Project Leyden's condensers, things like this might work a bit differently.

like image 145
dan1st Avatar answered Jul 18 '26 11:07

dan1st


As @dan1st mentioned, this is due to the way the intern() method works in Java.

first main method

  1. You are creating a new String object a using a StringBuilder and then call toString() on it. This creates a new String object with the value "Jav."

  2. Then, you are calling a.intern(), which attempts to add the string "Jav" to the string pool. But there is already a string "Jav" in the string pool (because of new StringBuilder("jav");). So, the intern()-call does nothing and a still refers to the newly created String object.

  3. Finally, when you compare a and c using a == c, it checks if they both are referring to the same object. In this case, they do not because a refers to the newly created String object, and c refers to the "Jav" string in the string pool. Hence, the result is false.

second main method

  1. You are creating a new String object a using a StringBuilder by appending "Ja" and "v" together. This creates a new String object with the value "Jav".

  2. Then, you are calling a.intern(), which attempts to add the string "Jav" to the string pool. But, as the string "Jav" is not in the string pool, it adds it, and the reference to the string in the string pool is returned.

  3. And now, when you compare a and c using a == c, they are referring to the same string object in the string pool (the one created by a.intern()), which returns the result as true.

like image 32
Nidheesh R Avatar answered Jul 18 '26 12:07

Nidheesh R