如果在J中使用字符串串联,则为“ ==”

    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

a和b都指向字符串常量池中的同一String Literal。 所以true在情况1下

String d = "dev" + "ender";

应该在内部使用类似-

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

a和d如何指向同一参考,而不是a和e?

dev ツ asked 2020-01-24T12:54:56Z
7个解决方案
79 votes

发生了四件事:

  1. (您显然知道这一点,但对于潜伏者而言)==测试以查看变量是否指向相同的==对象,而不是等效的字符串。 因此,即使equalse"devender"也是StringBuilderString可能是true还是false,这取决于eString引用的是相同的a160对象还是不同的对象。 这就是为什么我们使用==而不是a == e来比较字符串的等效性的原因。 以下所有内容只是为了解释为什么有时==是正确的,并不是建议使用==比较字符串。 :-)

  2. 相同的类中的等效字符串常量(编译器知道的字符串是根据JLS中各种规则的常量)由编译器(它也在类的“常量池”中列出)引用相同的字符串。 这就是为什么==是正确的。

  3. 加载该类时,将自动实习其每个字符串常量-在JVM的字符串池中检查等效字符串,如果找到一个等效字符串,则使用==对象(如果没有,则为新常量添加新的equals对象 到游泳池)。 因此,即使e是在类"devender"中初始化的字符串常量和StringBuilder是在类String中初始化的字符串常量,它们也将彼此为e

    JLS§3.10.5部分覆盖了以上第2点和第3点。 (有关类常量池的内容仅包含实现细节,因此可以更早地链接到JVM规范; JLS只是讲到了内部。)

  4. 编译器在处理常量值时会进行字符串连接,因此

    ==

    编译为

    ==

    ==是一个字符串常量,编译器和JVM将上面的第2点和第3点应用于该常量。 例如,如果不使用equals,则级联会在编译时而不是运行时发生。 JLS§15.28-常量表达式中对此进行了介绍。 所以e是正确的,原因是"devender"是正确的:它们引用相同的常量字符串,因此编译器确保它们引用的是类的常量池中的相同字符串。

    当任何一个操作数都不是常量时,编译器将无法执行此操作,因此它无法使用以下操作:

    ==

    ...尽管代码分析可以轻松地显示==的值肯定为equals,因此e154的绝对值将为"devender"155。规范仅允许编译器使用常量值进行串联。 因此,由于编译器无法执行此操作,因此它将输出您引用的StringBuilder代码,并且该工作已在运行时完成,从而创建了一个新的String对象。 该字符串不会自动插入,因此e最终会引用与a160不同的String对象,因此a == e为false。

    请注意,正如Vinod所说,如果您将==声明为equals

    ==

    然后它将是一个常量变量(是的,它们确实被称为),因此将应用§15.28,并且编译器将打开

    ==

    进入

    ==

    ==也将是正确的。

重申一下:这些都不意味着我们应该使用==比较字符串的等效性。 :-)这就是equals的用途。

T.J. Crowder answered 2020-01-24T12:56:39Z
16 votes

编译器在后台进行了大量优化。

c

在这里,编译器将在编译程序时将c替换为c。 如果要添加2个文字(这适用于原语和字符串),则编译器会进行此优化。

Java代码:

String d = "dev" + "ender";

字节码:

  0: ldc           #16                 // String devender

出现特殊情况:

c

使c最终将使String成为编译时常数。 编译器将意识到c的值不能更改,因此在编译时将所有出现的c替换为值“ dev”,因此e将在编译时自行解决。

TheLostMind answered 2020-01-24T12:57:30Z
9 votes

ec + "ender"之间的区别在于,当连接字符串文字时,将在编译时执行连接。 Java编译器以与==表达式相同的方式处理String表达式,在编译时产生相同的文字。 由于所有String文字均会被保留,因此"dev" + "ender"的结果d也最终引用了与ab"devender"相同的对象。

在运行时评估e的表达式c + "ender"。 即使产生相同的字符串,编译器也不会使用此事实。 这就是为什么要生成另一个String对象,导致在==上进行比较失败的原因。

dasblinkenlight answered 2020-01-24T12:57:56Z
9 votes

正如您在内部所说的,最后的连接是对类似于

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

.equals()String的实现创建了一个新的String。 这是实现。

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

仅当两个字符串相同时,才使用String而不是.equals()比较字符串将返回String。 在这种情况下,它们是不一样的,因为第二个字符串被创建为String类型的新对象。

其他串联由编译器直接执行,因此不会创建新的String。

Davide Lorenzo MARINO answered 2020-01-24T12:58:30Z
9 votes

a是一个可在编译时评估的常量表达式:两个参数都是字符串文字。 因此,表达式为b

对于a,不能说相同的话:某些情况(例如,某些代码在不同的线程上运行)可能导致b设置为不同的值。 将d限定为final可以消除这种可能性,在这种情况下,e也将引用与a相同的对象。

因此abd都引用相同的对象。

Bathsheba answered 2020-01-24T12:58:59Z
6 votes

字符串d =“ dev” +“ ender”; 常数+常数,“ d”仍然是常数(相同),因此(a == d)为true;

字符串e = c +“ ender”; 变量+常量,结果'e'是一个变量,它将在内部使用StringBuilder,并创建一个新引用。

maolazhu answered 2020-01-24T12:59:24Z
0 votes

请记住,Java保留了在程序中找到的所有字符串文字的池,用于其他目的的匹配,因此上面的任何不同的字符串文字串联都将导致相同的对象和相同的字符串文字。 您可以查看这篇有用的文章以了解更多信息。

另一方面,将String对象和文字(大小写c + "ender")连接起来将导致在运行时创建as StringBuilder对象,这与在池中找到的文字不同。

Nick Louloudakis answered 2020-01-24T12:59:49Z
translate from https://stackoverflow.com:/questions/34509566/in-case-of-string-concatenation-in-java