&引用==&引用;在Java中进行字符串连接的情况下

&引用==&引用;在Java中进行字符串连接的情况下,java,string,Java,String,a和b都指向字符串常量池中的相同字符串文本。因此,在案例1中true String a = "devender"; String b = "devender"; String c = "dev"; String d = "dev" + "ender"; String e = c + "ender"; System.out.println(a == b); //case

ab都指向字符串常量池中的相同字符串文本。因此,在案例1中
true

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
应该在内部使用类似于:

String d = "dev" + "ender";

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

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

String d = new StringBuilder().append("dev").append("ender").toString();
StringBuilder
toString()
的实现创建了一个新字符串。下面是实现

String e = new StringBuilder().append(c).append("ender").toString();
仅当两个字符串相同时,使用
==
而不是
.equals()
比较字符串会返回
true
。在这种情况下,它们是不同的,因为第二个字符串是作为类型为
string
的新对象创建的


其他连接由编译器直接执行,因此不会创建新字符串。

发生了四件事:

  • (你很清楚这一点,但对于潜伏者来说)
    =
    测试变量是否指向相同的
    字符串,而不是等价的字符串。因此,即使
    x
    “foo”
    并且
    y
    也是
    “foo”
    x==y
    可能是真或假,这取决于
    x
    y
    是指相同的
    字符串对象还是指不同的对象。这就是为什么我们使用而不是
    ==
    ,来比较字符串的等价性。以下所有内容只是为了解释为什么
    =
    有时是正确的,并不建议使用
    =
    来比较字符串。:-)

  • 同一类中的等效字符串常量(编译器知道的字符串是根据JLS中的各种规则确定的常量)由编译器引用同一字符串(编译器也在类的列表中列出它们)。这就是为什么
    a==b
    是正确的

  • 加载该类时,会自动加载其每个字符串常量-检查JVM的字符串池是否有等效字符串,如果找到,则使用
    string
    对象(如果没有,则将新常量的新
    string
    对象添加到池中)。因此,即使
    x
    是在类
    Foo
    中初始化的字符串常量,
    y
    是在类
    Bar
    中初始化的字符串常量,它们也将彼此
    =

    上文第2点和第3点在本节的部分内容中有介绍。(关于类常量池的部分是一个实现细节,因此前面有到JVM规范的链接;JLS只提到了interning。)

  • 如果编译器处理的是常量值,它会进行字符串连接,因此

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

    String d = "dev" + "ender";
    
    “devender”
    是编译器和JVM应用上述第2点和第3点的字符串常量。例如,如果不使用
    StringBuilder
    ,则连接发生在编译时,而不是运行时。这是一本书。因此
    a==d
    为true,原因与
    a==b
    为true相同:它们引用相同的常量字符串,因此编译器确保它们引用的是类的常量池中的相同字符串

    当任何操作数不是常量时,编译器都无法执行此操作,因此在以下情况下无法执行此操作:

    String d = "devender";
    
    …即使代码分析可以很容易地表明
    c
    的值肯定是
    “dev”
    ,因此
    e
    肯定是
    “devender”
    。该规范只让编译器使用常量值进行连接,具体来说。因此,由于编译器无法执行此操作,它将输出您提到的
    StringBuilder
    代码,并且该工作将在运行时完成,从而创建一个新的
    String
    对象。该字符串不会自动插入,因此
    e
    最终引用的
    string
    对象与
    a
    不同,因此
    a==e
    为false

    请注意,如果您将
    c
    声明为
    final

    String e = c + "ender";
    
    然后是a(是的,它们真的被称为a),因此§15.28将适用,编译器将

    final String c = "dev";
    
    进入

    而且
    a==e
    也是正确的

  • 只是重申一下:这一切都不意味着我们应该使用
    =
    来比较字符串的等价性。:-)这就是
    equals
    的作用。

    “dev”+“ender”
    是一个编译时可计算的常量表达式:两个参数都是字符串文本。因此,表达式是“devender”

    对于
    c+“ender”
    ,情况并非如此:某些情况(例如,某些代码在不同线程上运行)可能会导致
    c
    被设置为不同的值。将
    c
    限定为
    final
    可以避免这种可能性,在这种情况下
    e
    也将引用与
    a
    相同的对象


    因此
    a
    b
    d
    都指向同一个对象。

    d
    e
    之间的区别在于,当您连接字符串文本时,连接是在编译时执行的。Java编译器处理
    “dev”+“ender”
    表达式的方式与处理
    “devender”
    表达式的方式相同,在编译时生成相同的文本。由于所有
    String
    文本都被插入,
    d
    ,这是
    “dev”+“ender”
    的结果,也最终引用了与
    a
    b
    “devender”
    相同的对象


    e
    的表达式是
    c+“ender”
    ,在运行时进行计算。即使它生成相同的字符串,编译器也不会使用这个事实。这就是为什么会生成不同的
    字符串
    对象,导致在
    =

    上的比较失败。编译器会在后台进行大量优化

    这里是编译器wil
    String e = c + "ender";
    
    String e = "devender";
    
    String d = "dev" + "ender";
    
    String d = "dev" + "ender";
    
      0: ldc           #16                 // String devender
    
    final String c = "dev"; // mark this as final
    String e = c + "ender";