2

Why these two similar code snippets lead to different results?

public class Test {
    @org.junit.Test
    public void test1() {
        String s3 = "1" + new String("1");
        String s5 = s3.intern();
        System.out.println(s5 == s3);
    }

    @org.junit.Test
    public void test2() {
        String s3 = "g" + new String("g");
        String s5 = s3.intern();
        System.out.println(s5 == s3);
    }
}   

Tried in jdk 1.8.0_144 environment, got these answer (maybe different in jdk6):

false and true

The first snippet should be true in Java8. But more confusingly, If I moved the code into main method, It will result in true.

public class TestWithoutJunit {
    public static void main(String[] args) {
        String s3 = "1" + new String("1");
        String s5 = s3.intern();
        System.out.println(s5 == s3);
    }
}

I assume this is kind of JUnit optimizing, so I check bytecode, two code snippet inside jUnit and main function remain exactly same.

By the way, my Java version

java version "1.8.0_144" Java(TM) SE Runtime Environment (build 1.8.0_144-b01) Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
Stylex
  • 49
  • 4
  • Possible duplicate of [How do I compare strings in Java?](https://stackoverflow.com/questions/513832/how-do-i-compare-strings-in-java) – André Stannek Jul 16 '18 at 08:24
  • @AndréStannek Not if you understand the question. – user207421 Jul 16 '18 at 08:38
  • Maybe gg is already interned somewhere else – jontro Jul 16 '18 at 08:38
  • @jontro `"gg"` is already interned by the fact that it is a string literal. JLS requires it. I strongly suspect a typo in the first method. – user207421 Jul 16 '18 at 08:39
  • anyways you would need to look at the IR code. The first line could be optimized – jontro Jul 16 '18 at 08:40
  • 2
    Cannot reproduce. `java version "1.8.0_172" Java(TM) SE Runtime Environment (build 1.8.0_172-b11) Java HotSpot(TM) 64-Bit Server VM (build 25.172-b11, mixed mode)`. – user207421 Jul 16 '18 at 08:45
  • 1
    ran these snippets on jdk10, got all `true`s (as it should be) – Andrew Tobilko Jul 16 '18 at 08:45
  • @Stylex you should accept one of answer by using that checkmark next to voting arrows - do this for your old questions too ;) As seems that you never accepted any answer in your questions. – GotoFinal Jul 16 '18 at 11:30
  • If I move all code snippet from JUnit to `public static void main(String[] args)`, two result will be all true. – Stylex Jul 16 '18 at 11:59
  • If I only run this code: String s3 = "1" + new String("1"); String s5 = s3.intern(); System.out.println(s5 == s3); Result will be false in JUnit. If I move this code snippet from JUnit into public static void main(String[] args), result will be true. I have checked two version's bytecode, It would be the same. And I checked Constant Pool, Can't find anything like "11" before this line: String s4 = "11"; – Stylex Jul 16 '18 at 12:53
  • 1
    @Stylex Probably because JUnit is causing that sun.text.resources.FormatData class to load before your code. – GotoFinal Jul 16 '18 at 13:03
  • @EJP I have downloaded jdk10, still got same result. you should try in junit instead of main method – Stylex Jul 16 '18 at 14:27
  • @AndrewTobilko I have downloaded jdk10, still got same result. you should try in junit instead of main method – Stylex Jul 16 '18 at 14:28

2 Answers2

3

Presumably gg is taken place in String Pool though I don't know how come, maybe Junit did some prior things under the hood. If a string literal is in string pool, it is referenced again in lieu of instantiating again. That's why you get true in the second part of your code with == operator. You should always use equals method for certain string comparison.

Do you understand what intern() method is actually doing?

String objects can be added to this pool using String's intern() method. For any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true.

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

@Edit: OP said as comment,

I checked Constant Pool, Can't find anything like "11" before this line: String s4 = "11";

You are not able to access the string pool from Java code, at least not in the HotSpot implementation of Java VM. So that's why you can't see 11 before String s4 = "11"

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
  • If I only run this code: String s3 = "1" + new String("1"); String s5 = s3.intern(); System.out.println(s5 == s3); Result will be false in JUnit. If I move this code snippet from JUnit into public static void main(String[] args), result will be true. I have checked two version's bytecode, It would be the same. And I checked Constant Pool, Can't find anything like "11" before this line: String s4 = "11"; – Stylex Jul 16 '18 at 12:53
  • @Stylex edited as well, _maybe `Junit` did some prior things under the hood._ – Soner from The Ottoman Empire Jul 16 '18 at 13:14
  • OP probably checked constant pool of class, not string pool – GotoFinal Jul 16 '18 at 13:43
  • @GotoFinal Yes, I wrote it to highlight where he is looking for. – Soner from The Ottoman Empire Jul 16 '18 at 13:52
1

Like others says, you should use .equals to compare string, but if you wanted to know why exactly in this case you have different results then:

Each string literal is added to string pool by default and because "1" string is already used as literal in different class, then .intern is returning different instance, for "g" there are no other usages of such literal, so your one is the first added to the string pool - so .intern does not change anything in this case - as it returns this same instance as yours.
Same for "11" and "gg" string.
To be exact, on JDK 8 it seems to be used by sun.text.resources.FormatData. (both strings, "1" and "11")

If you would add other class with "g" literal that would load before this one - you would get same results as for "1".

GotoFinal
  • 3,585
  • 2
  • 18
  • 33
  • If I only run this code: String s3 = "1" + new String("1"); String s5 = s3.intern(); System.out.println(s5 == s3); Result will be false in JUnit. If I move this code snippet from JUnit into public static void main(String[] args), result will be true. I have checked two version's bytecode, It would be the same. And I checked Constant Pool, Can't find anything like "11" before this line: String s4 = "11"; – Stylex Jul 16 '18 at 12:51
  • 1
    @Stylex Probably because JUnit is causing that `sun.text.resources.FormatData` class to load. – GotoFinal Jul 16 '18 at 13:02