关于String a =“你好”;字符串b =“hello”a == b,在java中

时间:2011-06-26 05:32:42

标签: java string

检查以下程序: 在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";
}

6 个答案:

答案 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的说法,那么


注意:

  1. 有什么和不是常量表达式的定义有点牵扯。你可能想象的一些不变的东西实际上并非如此。例如,"hello".length()不是常量表达式。但是,两个字符串文字的串联是一个常量表达式。

  2. 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==bfalse

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==btrue