我知道什么是区别'=='与'等于'和JVM字符串回收在字符串常量池!
但这有点奇怪。
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... !!!
我想看看其他类中的常量是否不能被回收,所以我在其他类中使用了字符串,如下所示。
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
}
}
当然,这就造成了true,为什么当我用反射得到类的名字时,会出现false呢?
1条答案
按热度按时间c9qzyr3d1#
这是一个实现细节,但对于顶级类,
getSimpleName()
的结果是substring
操作的结果,如这样的字符串操作的结果永远不会成为字符串池的一部分,因此,要将其添加到池中,需要显式调用
intern()
。但是
intern()
并不是一个便宜的操作。This answer列出了一些问题,比如需要线程安全更新或者(在HotSpot JVM的情况下)具有固定的哈希表大小,因此当添加太多元素时,由于冲突而变得效率低下。由于不知道调用者的意图,因此不知道这些成本是否会得到回报。在JDK11之前,甚至没有缓存,因此即使
String.class.getSimpleName() == String.class.getSimpleName()
也会计算为false
。(软可访问,因此它仍然可以被垃圾收集),但是将字符串添加到池中仍然只有在存在字符串常量或相同内容的显式驻留字符串的 other 出现时才有回报,而这是不能预先假定的。这是一个不同的情况与限定名,或更正式的,* 二进制名称 *,类将永久关联。这是一个名称用于查找类加载器和遇到
Class.forName("full.qualified.ClassName")
的可能性高于遇到特定的"ClassName"
某处。这个字符串是由本机代码无论如何,它的代码路径与解析字符串常量的代码路径相似。因此,
String.class.getName() == "java.lang.String"
在此实现中始终解析为true
,但由于文档中没有指定,因此不应依赖它。