2010-09-26 210 views
4
public class Demo { 

    public static void main(String[] args) { 

     String s1 = "Hello"; 
     String s2 = "Hello"; 
     System.out.println("s1 == s2 " + (s1 == s2)); 

     String s5 = "Hel" + "lo"; 
     String s6 = "He" + "llo"; 
     System.out.println("s5 == s6 " + (s5 == s6)); 

     String s7 = "He"; 
     String s8 = "Hello"; 
     s7 = s7.concat("llo"); 
     System.out.println("s7 == s8 " + (s7 == s8)); 

     String s10 = "He"; 
     s10 = s10 + "llo"; 
     System.out.println("s1 == s10 "+(s1 == s10)); 
    } 
} 

在前面的代码中,s7 == s8和s1 == s10给出false。有人可以解释我,在这里实际发生了什么s7 = s7.concat(“llo”);和s10 = s10 +“llo”;我明白==运算符检查引用和equal()检查对象的内容。但我需要知道为什么s7和s10引用变量的位模式与s8和s1不同。如果这些东西与编译时生成的字符串和运行时生成的字符串有关,那么我怎样才能确定它是编译时还是运行时字符串?关于java中==运算符的问题

+0

你是什么意思的“参考变量位模式”? – Cameron 2010-09-26 19:37:29

+0

String问题上的另一个'=='。似乎在这个相同的问题上的变化被频繁地询问。难道不应该有标准答案,并且所有后续事件都会被重复关闭吗? – 2010-09-26 22:05:53

+0

@cameron我从一些书中学到了如果一个引用变量c引用了对象X017432并且引用变量d也引用了对象X017432,那么c和d中的位就是相同的。所以我需要知道为什么s7和s10引用不等于已经引用字符串池中的“Hello”的s8和s10。 – ddfnfal 2010-09-27 06:46:52

回答

11

发生这种情况的原因是因为Java正在编译器中进行优化。当它看到你将字符串"Hello"分配给s1时,它对s2使用相同的“Hello”,因为所有的Java String操作都是非破坏性的(例如它们返回一个克隆而不是修改原始),所以这是一个安全的事情要做。

同样的事情发生在"Hel" + "lo" vs "He" + "llo";足够聪明地弄清楚它们是一回事。

其他很复杂,它不能优化它们,因此你最终得到单独的对象。

+5

“Hel”+“lo”'和“He”+“llo”的优化是[常量折叠](http://en.wikipedia.org/wiki/Constant_folding)的结果。 – 2010-09-26 19:44:50

4

==不检查位模式,它会比较对象的内存地址。只有同一个对象具有相同的内存地址。

+2

这是我的原始答案,这不是他要问的。 – 2010-09-26 19:38:58

+0

@ Clint Tseng:呃,你说的对,我的不好。马克确实感到困惑,并认为它必须以某种方式平等:p – 2010-09-26 19:43:06

0

等号运算符测试引用是否相同(即指向相同的对象),而不是引用的值是否相同。如果您需要测试一个字符串是否等于另一个字符串,则应使用内置的.equals方法。这将做一个对象值比较。例如

final String expectedValue = "Foo"; 
final String actualValue = "F" + "oo"; 
if (expectedValue.equals(actualValue)) { 
    // This will trigger where == would not 
} 

另外为了安全起见,如果你做的比较两个字符串,一个是恒定的,它通常是最好的调用等于在恒定的,即

String myValue = getMyValue; 
boolean theSame = "Some value".equals(myValue); 

不是

String myValue = getMyValue; 
boolean theSame = myValue.equals("Some Value"); 

原因是第二种形式存在空指针异常的风险,可以通过在保证存在的常量字符串上调用equals()来避免这种情况。

0

您无法对字符串对象进行任何假设。

虚拟机可以努力确保没有两个包含完全相同字符数组的字符串对象同时存在,而其他虚拟机允许重复。

0

==运算符仅检查两个对象是否具有相同的地址(指针)。只适用于不是引用的基本类型(如int,char等),它会比较值。

您需要使用类似s1.equals(s2)的内容来比较两个字符串的内容。

0

在这个例子中,你提供的,这是发生了什么事:

String s7 = “He”; //s7 is an object referencing a part of memory holding “He” 
String s8 = “Hello”; //s8 is an object referencing a part of memory holding “Hello” 
s7 = s7.concat(“llo”); //s7.concat(“llo”) created a new object in memory that contains “Hello” and s7 now references this now object 

(s7==s8)    //checks if the equality of the object reference and this is false since they reference different memory addresses. 

(s7.equals(s8))   //this will compare s7 and s8 however way the String class compares two String objects. 
4

克林特的回答是不错,但我会在进一步扩大它在编译器级别解释。

如您所知,s1s2将最终引用相同的字符串实例"Hello"

对于s5s6,编译器会看到常量表达式。也就是说,它看到两个常量(字符串文字)之间的操作。编译器知道如何添加字符串以及结果会是什么。由于这些值是在编译时立即知道的,因此会为您添加新的值,从而生成字符串"Hello"。因此,它与s1s2具有相同的值,因此每个参数也会引用同一个实例。

s7不能用同样的方法简化。 s7当然最初以"He"开头。区别在于s7 = s7.concat("llo");s7重新分配给函数调用的结果。这不能被简化。就Java编译器而言,所有函数调用的结果在编译时都是未知的。由于它不知道结果值,因此无法简化并保持原样。结果调用返回"Hello"字符串的新实例,该实例与编译时实例(s8共享)的实例不同。

s10也不能简化为同样的方式。 s10当然最初以"He"开头。然后重新分配s10 = s10 + "llo";这不能被简化。你为什么会问?那么s10是一个非最终变量表达式。从技术上讲,编译器不知道它的价值,因为它不是一个常量。如果s10被声明为final String,那么这可以是不断折叠的(当分配给不同的变量时)。

所以考虑这个版本的测试代码:

public static void main(String[] args) 
{ 
    String s1 = "Hello"; 
    String s2 = "Hello"; 
    System.out.println("1: s1 == s2 " + (s1 == s2)); // 1 

    String s3 = "Hel" + "lo"; 
    String s4 = "Hel".concat("lo"); 
    System.out.println("2: s1 == s3 " + (s1 == s3)); // 2 
    System.out.println("3: s1 == s4 " + (s1 == s4)); // 3 

    String he = "He"; 
    String s5 = he + "llo"; 
    String s6 = he.concat("llo"); 
    System.out.println("4: s1 == s5 " + (s1 == s5)); // 4 
    System.out.println("5: s1 == s6 " + (s1 == s6)); // 5 

    final String fhe = "He"; 
    String s7 = fhe + "llo"; 
    String s8 = fhe.concat("llo"); 
    System.out.println("6: s1 == s7 " + (s1 == s7)); // 6 
    System.out.println("7: s1 == s8 " + (s1 == s8)); // 7 
} 

你能找出哪些线是真的吗?

真的,真的,假的,假的,假的,真的,假的
你可能会奇怪,为什么3和7是不正确的。简而言之,Java编译器没有被编程
要足够聪明才能识别concat()调用,因此被视为常规函数调用。

+0

谢谢,但我有一些疑惑,你为什么指的是s7.concat(“llo”);不能简化。这是否意味着,“Hello”字符串不是在编译时通过concat方法生成的?如果是,它会创建新的对象(Hello)而不是池中现有的“Hello”对象? – ddfnfal 2010-09-27 05:22:59

+0

对不起,我以为我解释了比实际写的更多。我会详细说明。 – 2010-09-27 05:28:05