junit-team / junit4

A programmer-oriented testing framework for Java.
https://junit.org/junit4
Eclipse Public License 1.0
8.51k stars 3.24k forks source link

Confusing problem... get differernt result between `main method` and `test method` #1738

Closed Gakiii closed 2 years ago

Gakiii commented 2 years ago

when I run such code in a main method I got the result like this Main.java

public class Main {
    public static void main(String[] args) {
        String s = String.valueOf(1);
        s.intern();
        String s2 = "1";
        System.out.println(s == s2); // true
    }
}

but I run it with junit like this StringTest.java

public class StringTest {
    @Test
    public void Test1() {
        String s = String.valueOf(1);
        s.intern();
        String s2 = "1";
        System.out.println(s == s2); //false
    }
}

chips : Apple M1 OS : Mac Monterey 12.3.1 jdk verison:

❯ java -version
openjdk version "11.0.10" 2021-01-19 LTS
OpenJDK Runtime Environment Zulu11.45+27-CA (build 11.0.10+9-LTS)
OpenJDK 64-Bit Server VM Zulu11.45+27-CA (build 11.0.10+9-LTS, mixed mode)

junit verison: 4.13.2

panchenko commented 2 years ago

@Gakiii There is a bug in your code, the proper way to write it is s = s.intern();

Gakiii commented 2 years ago

There is a bug in your code, the proper way to write it is s = s.intern();

Of course I can write the code like you said, but in this case, I just want to use the method intern and don't want use its return value, I don't want to change the adress of s. In my way, I put the String "1" to the String constant pool, and don't change the adress of the variable s. And because there is a "1"(String s) in the heap, so intern method can just use the reference of s. That is the reason why in the main method I print (s == s2) is true. But I don't understand why I run the same code with junit, I get the different result. That is the reason I create this issue. You can try my code in your pc. I think you can get the same result.

Gakiii commented 2 years ago

r code, the proper way to write it is s = s.intern();

I guess when I run this code with junit, the String constant pool already has a "1"(like jdk8+ the "java" is already created by the jvm). so the code s.intern() in the junit file actually do nothing. so s == s2 is flase. That the only reason I can explain this. @panchenko

panchenko commented 2 years ago

@Gakiii Actually I cannot explain how you get true.

"1" is always in the pool., as the constants are interned:

 * All literal strings and string-valued constant expressions are
 * interned. String literals are defined in section {@jls 3.10.5} of the
 * <cite>The Java Language Specification</cite>.

However I am not aware of String.valueOf(int) returning interned strings and I don't see that in the sources.

Gakiii commented 2 years ago

@Gakiii Actually I cannot explain how you get true.

@panchenko emmmmm, I'm really confused You can try it. And I'm sure you will get true like me.

image
Gakiii commented 2 years ago

@panchenko

image

when I don't use s.intern() the result is false 😟

marcphilipp commented 2 years ago

I think the difference is that the main method is static so the compiler seems to be generating an interned constant for your string whereas it doesn't do that for the non-static test method. If you're code relies on interning, you should make sure to call intern(). I think you'd get the same result if you moved your code to a static method that's called from main() and your test method.