0

i know what is difference '==' with 'equals' and JVM String recycle in String Constant pool!

but this is some weird.

String m1 = "aa".getClass().getSimpleName();
String m2 = "String";

System.out.println("m1 = " + m1); // String
System.out.println("m2 = " + m2); // String
System.out.println(m1 == m2); // false... !!!

I wanted to see if constants in other classes could not be recycled, so I used strings in other classes as follows.

public class GenerateString {

    public String foo() {
        return "String";
    }
}

public class Client {

    public static void main(String[] args) {
        GenerateString generateString = new GenerateString();
        String m1 = generateString.foo();
        String m2 = "String";


        System.out.println("m1 = " + m1); // String
        System.out.println("m2 = " + m2); // String
        System.out.println(m1 == m2); // true
    }
}

This, of course, caused true as expected. Why does false come out when I get the name of the class with reflection?

majava
  • 45
  • 9
  • `==` compare **references** and returns `true` if and only if *references* are equal. In your case `m1` and `m2` while having equal values doesn't share the same reference. To compare **values** put `equals` – Dmitry Bychenko Apr 13 '22 at 07:29
  • Like I said, I know the difference between the two. And if you use a string, it should return true even if you compare the reference because it is recycled from the constant pool. On the other hand, it is a question about why the reference is different because it is not used in the constant pool with reflection. – majava Apr 13 '22 at 07:35
  • 2
    Why would you think that `getSimpleName` should return a string from the pool? You cannot assume that every string returned from anywhere is coming from the pool. – khelwood Apr 13 '22 at 07:50
  • Conversely, let me ask you a question. So why not use it in the constant pool? Comparing GenerateString.foo with the reference value is always the same. Not only that, the reference value is the same when assembling a string, so why is it an exception in that situation? – majava Apr 13 '22 at 07:53
  • With my limited knowledge, two reasons why not to use the string from the pool. (1) Looking up in the pool takes time that would in 99.9 % of cases be a waste. (2) To teach young programmers that they need to use `equals()` between strings (and between objects in general). – Ole V.V. Apr 13 '22 at 07:57
  • I know I must use `equals()` when comparing strings. and strings are retrieved from the `contant pool` and reused. This is the JVM architecture. But my question is in that case the question is why the reference values ​​are different. – majava Apr 13 '22 at 07:59
  • Because one string is in the pool and the other is not. – Ole V.V. Apr 13 '22 at 08:00
  • 1
    If you think that the name of every class should be interned and retrieved from the string pool, what makes you think that would be beneficial or worthwhile? There are a lot of classes and many programs won't need to get their names. – khelwood Apr 13 '22 at 08:00
  • I don't think this is beneficial. But I've never seen such an exception in the JVM documentation. Now that I check, `getSimepleName` seems to have a cached value. I asked you why `getSimeName` don't use the constant pool. – majava Apr 13 '22 at 08:06
  • It’s no exception. To the best of my knowledge the rule is that string literals are in the pool, other strings are only if you explicitly add them. A class name is no string literal. – Ole V.V. Apr 13 '22 at 10:16
  • 2
    @OleV.V. actually, the class same is in the pool. The simple name is not. – Holger Apr 13 '22 at 14:12
  • 2
    @majava you’ve “never seen such an exception in the JVM documentation” because the behavior of being in the pool has never been specified for any of these API methods. Since not being in the pool is not an exception to any rule, there’s no need to specify that there was an exception. – Holger Apr 13 '22 at 14:13

1 Answers1

4

This is an implementation detail, but for a top level class, the result of getSimpleName() is the result of a substring operation, like

Class<?> cl = String.class;
String s = cl.getName();
s = s.substring(s.lastIndexOf('.') + 1);

Results of such string operations are never part of the string pool. So, to add it to the pool, an explicit invocation of intern() would be needed.

But intern() is not a cheap operation. This answer lists some of the problems, like requiring thread safe updates or (in case of the HotSpot JVM) having a fixed hash table size, hence becoming inefficient due to collisions when too many elements are added.

Since nothing is known about the caller’s intentions, it’s not known whether these costs would pay off. Prior to JDK 11, there was not even a caching so even String.class.getSimpleName() == String.class.getSimpleName() would evaluate to false. Now, there’s a cache (softly reachable, so it still could get garbage collected), but adding the string to the pool would still only pay off if there are other occurrences of string constants or explicitly interned strings of the same contents, which can not be assumed in advance.

This is a different situation with the qualified name or, more formally, the Binary Name to which the class will be permanently associated. This is the name used for lookups at class loaders and the likelihood of encountering Class.forName("full.qualified.ClassName") is higher than encountering the specific "ClassName" somewhere. This string is constructed by native code anyway, which goes a similar code path than the code resolving string constants.

So, String.class.getName() == "java.lang.String" always resolves to true with this implementation, but since this has not been specified in the documentation, you should not rely on it.

Holger
  • 285,553
  • 42
  • 434
  • 765