检查以下程序: 在sun java hostspot jvm中运行它,一切都将是“true”。
--------更新:得到了Stephen和Danie的答案,改变了程序添加字符串实习方法----------- 如果B单独编译而不是与A一起编译,将会发生什么?例如,B编译并放入jar中,并在运行TestStringEqual时放入其类路径? 另外,这是java编译时优化,还是java运行时优化,还是java语言规范的定义?此外,该程序在不同的虚拟机上只有一个虚拟机功能,结果相同吗?
谢谢
public class TestStringEqual {
public static String HELLO = "hello";
private String m_hello;
public TestStringEqual() {
m_hello = "hello";
}
public static void main(String[] args) {
String a = "hello";
String b = "hello";
System.out.println("string a== string b:" + (a == b));
System.out.println("static memebr ==a:" + (HELLO == a));
System.out.println("instance field ==a:"
+ (new TestStringEqual().getHello() == a));
System.out.println("hello in B ==a:" + (B.B_HELLO == a));
System.out.println("interned new string object in heep==a:"
+ ( new String("hello").intern() == a));
}
public String getHello() {
return this.m_hello;
}
}
class B{
public static final String B_HELLO = "he"+"llo";
}
答案 0 :(得分:3)
在JVM级别,LDC
(加载常量)指令用于将字符串文字推送到堆栈。出于性能原因,字符串文字不存储在代码本身中;它存储在类的常量池中。常量池是一个表,它出现在包含字符串文字,数字文字,字段和方法描述符以及其他一些内容的类文件的开头。 LDC
之后是一个字节,指定常量池中的字符串索引。 (如果一个字节不够大,编译器将使用LDC_W
,后面跟着一个16位偏移量。因此限制为65,536个常量。)
如果同一个字符串文字在同一个类中出现两次,那么javac足够聪明,只能在常量池中创建一个条目。加载类时,JVM会从常量池中的数据创建实际的String对象。因此,包含与常量池相同的偏移量的LDC
将导致相同的String被压入堆栈。像IF_ACMPEQ
这样的指令(检查引用相等性为==
)会将字符串识别为相同。
有关详细信息,请参阅JVMS。
答案 1 :(得分:3)
这根本没有任何神秘感。您只需要了解有关Java的三个基本事实:
对象引用的'=='运算符测试两个对象引用是否相同;即如果他们指向同一个对象。参考JLS 15.21.3
Java程序中具有相同字符序列的所有字符串文字将由相同的String对象表示。引用JLS 3.10.5所以(例如)"hello" == "hello"
正在比较同一个对象。
在编译时计算常量表达式。参考JLS 15.28。因此(例如)"hell" + "o"
在编译时进行评估,因此等同于文字"hello"
。
这三个事实在Java语言规范中有说明。它们足以解释程序的“令人费解”的方面行为,而不依赖于其他任何东西。
涉及字符串池的更详细的解释,由类加载器实现的字符串文字,编译器发出的字节码等等...... 只是实现细节。如果您了解JLS的说法,那么
注意:
有什么和不是常量表达式的定义有点牵扯。你可能想象的一些不变的东西实际上并非如此。例如,"hello".length()
不是常量表达式。但是,两个字符串文字的串联是一个常量表达式。
JLS中字符串文字相等的解释确实提到了实现文字属性的机制实习。
答案 2 :(得分:1)
这是一个不可变的字符串(无法变异或改变),不是免疫的,但我想你可以说它不受变化的影响: - )
这意味着您无法更改基础字符串本身,您只能为变量分配不同的字符串。所以:
string a = "Hello";
a = "Goodbye";
不会更改存储"Hello"
的内存,它会将a
更改为指向存储"Goodbye"
的其他内存位置。
这允许Java共享字符串以提高效率。您甚至可以获得"deoxyribonucleic acid"
和"acid"
等字符串可以共享空间的情况,后者指向前者中的特定位置。同样,这可以通过这种字符串的不可变性来实现。
在任何情况下,==
都会检查字符串是否引用相同的底层对象,而不是通常有用的东西。如果您想查看字符串是否相等,您应该使用String.equals()
或其中一个变体。
答案 3 :(得分:1)
这个链接会有帮助 - Questions about Java's String pool吗?
答案 4 :(得分:0)
这很简单:编译器会在第一次遇到字符串“hello”时生成(字节码)常量。在普通汇编程序中,它将位于.TEXT部分。
随后的“hello”字符串将指向同一个常量,因为不需要分配新空间或创建新常量。这样做的原因是因为字符串是不可变的,并且如果为其分配了一个新值,则无论如何都需要新的内存。
它可能不适用于输入,即如果你让用户输入“hello”和== - 将它与编译时hello字符串进行比较,你可能会得到错误。
答案 5 :(得分:0)
就a==b
而言,似乎编译器正在制作快捷方式并共享相同的字符串对象。当我声明我的变量如下时,我得到a==b
是false
。
String a = "hello";
String b = "hell";
String temp = "o";
if (new java.util.Random().nextDouble() < 0.5) b += temp;
else b += "o";
如果我String b = "hell"+"o";
我仍然a==b
为true
。