java 字符串 == 显示奇怪的结果
java String == shown strange result
我知道'=='和'equals'有什么区别
和 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... !!!
我想看看其他classes中的常量是不是不能回收,所以我使用其他classes中的字符串如下
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
}
}
这当然造成了预期的真实。为什么我用反射得到class的名字会出现false?
这是一个实现细节,但是对于顶层 class,getSimpleName()
的结果是 substring
操作的结果,例如
Class<?> cl = String.class;
String s = cl.getName();
s = s.substring(s.lastIndexOf('.') + 1);
此类字符串操作的结果永远不会成为字符串池的一部分。因此,要将其添加到池中,需要显式调用 intern()
。
但是intern()
并不是一个便宜的操作。 This answer 列出了一些问题,例如需要线程安全更新或(在 HotSpot JVM 的情况下)具有固定的散列 table 大小,因此在添加太多元素时由于冲突而变得低效。
由于对来电者的意图一无所知,因此不知道这些费用是否会得到回报。在 JDK 11 之前,甚至没有缓存,所以即使 String.class.getSimpleName() == String.class.getSimpleName()
也会评估为 false
。现在,有一个缓存(可以软访问,所以它仍然可以被垃圾收集),但是将字符串添加到池中仍然只有在字符串常量出现 other 次或显式出现时才会得到回报interned 相同内容的字符串,不能提前假设。
这是与限定名称不同的情况,或者更正式地说,class 将永久关联到 Binary Name。这是用于查找 class 加载程序的名称,遇到 Class.forName("full.qualified.ClassName")
的可能性高于在某处遇到特定 "ClassName"
的可能性。这个字符串无论如何都是由本机代码构造的,它的代码路径与解析字符串常量的代码相似。
因此,String.class.getName() == "java.lang.String"
始终通过此实现解析为 true
,但由于文档中未指定,因此您不应依赖它。
我知道'=='和'equals'有什么区别 和 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... !!!
我想看看其他classes中的常量是不是不能回收,所以我使用其他classes中的字符串如下
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
}
}
这当然造成了预期的真实。为什么我用反射得到class的名字会出现false?
这是一个实现细节,但是对于顶层 class,getSimpleName()
的结果是 substring
操作的结果,例如
Class<?> cl = String.class;
String s = cl.getName();
s = s.substring(s.lastIndexOf('.') + 1);
此类字符串操作的结果永远不会成为字符串池的一部分。因此,要将其添加到池中,需要显式调用 intern()
。
但是intern()
并不是一个便宜的操作。 This answer 列出了一些问题,例如需要线程安全更新或(在 HotSpot JVM 的情况下)具有固定的散列 table 大小,因此在添加太多元素时由于冲突而变得低效。
由于对来电者的意图一无所知,因此不知道这些费用是否会得到回报。在 JDK 11 之前,甚至没有缓存,所以即使 String.class.getSimpleName() == String.class.getSimpleName()
也会评估为 false
。现在,有一个缓存(可以软访问,所以它仍然可以被垃圾收集),但是将字符串添加到池中仍然只有在字符串常量出现 other 次或显式出现时才会得到回报interned 相同内容的字符串,不能提前假设。
这是与限定名称不同的情况,或者更正式地说,class 将永久关联到 Binary Name。这是用于查找 class 加载程序的名称,遇到 Class.forName("full.qualified.ClassName")
的可能性高于在某处遇到特定 "ClassName"
的可能性。这个字符串无论如何都是由本机代码构造的,它的代码路径与解析字符串常量的代码相似。
因此,String.class.getName() == "java.lang.String"
始终通过此实现解析为 true
,但由于文档中未指定,因此您不应依赖它。