在Java中的string连接的情况下“==”

String a = "devender"; String b = "devender"; String c = "dev"; String d = "dev" + "ender"; String e = c + "ender"; System.out.println(a == b); //case 1: o/p true System.out.println(a == d); //case 2: o/p true System.out.println(a == e); //case 3: o/p false 

ab都指向string常量池中相同的string。 在情况1中如此

 String d = "dev" + "ender"; 

应该在内部使用像 –

 String d = new StringBuilder().append("dev").append("ender").toString(); 

一个d如何指向相同的引用&而不是& e

四件事情正在发生:

  1. (你清楚地知道这一点,但对于潜伏者) ==testing,看看variables指向相同的String对象 ,而不是等价的string。 所以,即使x"foo"y也是"foo"x == y可能是true或false,取决于xy是指向相同的String对象还是不同的对象。 这就是为什么我们使用equals而不是==来比较string的等价性。 以下所有内容仅仅是为了解释为什么==有时候是正确的,不build议使用==来比较string。 🙂

  2. 等价的string常量(编译器知道的string是根据JLS中的各种规则的常量)被编译器引用到同一个string中(也列在类的“常量池”中 )。 这就是为什么a == b是真的。

  3. 当这个类被加载时,它的每个string常量都会被自动实现 – JVM的string池被检查一个等价的string,如果find了,就使用这个String对象(如果没有的话,新的常量的新的String对象被添加到游泳池)。 所以,即使x是一个在Foo类中初始化的string常量,而y是一个在Bar类中初始化的string常量,它们也将互为==

    以上第2点和第3点部分由JLS§3.10.5覆盖。 (关于类常量池的一点是一个实现细节,因此与之前的JVM规范有联系,JLS只是说实习。)

  4. 如果编译器处理常量值,则编译器会执行string连接

     String d = "dev" + "ender"; 

    被编译到

     String d = "devender"; 

    "devender"是一个string常量,编译器和JVM将上面的2和3点应用到。 例如,没有使用StringBuilder ,连接发生在编译时 ,而不是运行时。 这包含在JLS§15.28 – 常量expression式中 。 所以, a == d的原因与a == b是同一个原因:它们引用相同的常量string,所以编译器确保它们引用类的常量池中的相同string。

    当任何操作数不是一个常量时,编译器不能这样做,所以它不能这样做:

     String e = c + "ender"; 

    即使代码分析可以很容易地显示出c的价值肯定会是"dev" ,因此e一定是"devender" 。 规范只是让编译器用常数值连接,具体来说。 因此,由于编译器不能这样做,它会输出您引用的StringBuilder代码,并且该工作在运行时完成,从而创build一个新的String对象。 该string不会自动被拦截,所以e最后引用了一个不同的String对象,所以a == e是false。

    请注意, 正如维诺德所说 ,如果你宣布cfinal

     final String c = "dev"; 

    那么这将是一个常量variables (是的,他们真的被称为),所以§15.28将适用,编译器将转向

     String e = c + "ender"; 

     String e = "devender"; 

    a == e也是如此。

只是重申:没有哪一个意味着我们应该使用==比较string的等价性。 :-)这就是equals目的。

编译器在底层做了很多优化。

 String d = "dev" + "ender"; 

这里编译器会在程序编译时用"devender"代替"dev" + "ender" "devender" 。 如果要添加2个文字(这适用于基元和string),编译器会执行此优化。

Java代码:

 String d = "dev" + "ender"; 

字节码:

  0: ldc #16 // String devender 

来一个特例:

 final String c = "dev"; // mark this as final String e = c + "ender"; 

使c最终将使string编译时间不变 。 编译器会认识到c的值不能改变,因此在编译时会用c的值“dev”replacec所有出现,因此在编译时本身就会解决。

de之间的区别在于,当连接string文字时 ,连接在编译时执行。 Java编译器以与"devender"expression式相同的方式处理"dev" + "ender" "devender"expression式,在编译时产生相同的文字。 由于所有的String都被禁止了,因此"dev" + "ender"的结果d也会以ab"devender"引用同一个对象。

e的expression式,即c + "ender" ,在运行时被评估。 即使它产生相同的string,编译器也不会使用这个事实。 这就是为什么生成不同的String对象,导致==比较失败。

正如你在内部所说的,最后的连接是通过类似的方式完成的

 String e = new StringBuilder().append(c).append("ender").toString(); 

StringBuildertoString()的实现会创build一个新的String 。 这是实施。

 public String toString() { // Create a copy, don't share the array return new String(value, 0, count); } 

使用==比较string而不是.equals() 仅在两个string相同时才返回true 。 在这种情况下, 它们不一样,因为第二个string被创build为一个 Stringtypes的新对象。

其他连接由编译器直接执行,所以不会创build新的string。

"dev" + "ender"是一个编译时可评估的常量expression式:这两个参数都是string文字。 因此这个expression是"devender"

c + "ender"不能这样说的:某些情况下(例如某个代码运行在不同的线程上)可能导致c被设置为一个不同的值。 合格的c作为final消除这种可能性,在这种情况下, e也将同一个对象称为a

所以abd都是指同一个对象。

String d =“dev”+“ender”; 常数+常数,“d”仍然是一个常数(同一个),所以(a == d)是真的;

String e = c +“ender”; variables+常量,结果'e'是一个variables,它将在内部使用StringBuilder,并创build一个新的引用。

请记住,Java拥有一个在程序中find的所有string文字的池,用于匹配等目的,所以上面的任何不同的string连接将导致相同的对象,相同的string文字。 你可以看看这个有用的文章更多。

另一方面,一个String对象和一个文本(case c + "ender" )的连接将导致在运行时创build一个StringBuilder对象,与在池中find的文字不同。