字符串连接时Java字符串池的工作原理是什么?

xsa*_*ami 2 java string object-reference string-comparison string-pool

注意:我不是要比较角色是否等于.因为我知道如何使用String.equals()方法.这个问题是关于字符串引用的

当我开始学习类字符串及其属性作为不变性等时,我正在学习OCA考试.根据我读到或可能理解的字符串池是在创建字符串时,Java将此对象存储在他们调用的内容中字符串池,如果创建一个具有相同值的新字符串,它将引用字符串池上的字符串,除非我们使用new关键字,因为这会创建一个新引用,即使两个字符串包含相同的值.

例如:

String a = "foo";
String b = "foo";
String c = new String("foo");
boolean ab = (a == b); // This return true
boolean ac = (a == c); // This return false
Run Code Online (Sandbox Code Playgroud)

要清楚这段代码是在第一行代码中创建String a = "foo"并将其存储在String池中,而在第二行代码中它将创建String b和引用,"foo"因为它已存在于String池中.但是第3行将创建此字符串的新引用,无论它是否已存在.这是一个关于发生了什么的图形示例: http://cdn.journaldev.com/wp-content/uploads/2012/11/String-Pool-Java1.png

问题出现在以下代码行中.当串联创建字符串时,java会做出不同的东西或简单= =比较器有另一种行为吗?

例A:

String a = "hello" + " world!";
String b = "hello world!";
boolean compare = (a == b); // This return true
Run Code Online (Sandbox Code Playgroud)

例B:

a = "hello";
b = "hel" + "lo";
compare = (a == b); // This return true
Run Code Online (Sandbox Code Playgroud)

例C:

a = "Bye";
a += " bye!";
b = "Bye bye!";
compare = (a == b); // This return false
Run Code Online (Sandbox Code Playgroud)

要观看代码运行:(http://ideone.com/fdk6KL)

怎么了 ?

编辑

  1. 修复示例B中的错误: b = 'hel' + 'lo'

  2. 添加有关问题的说明.这不是比较问题,因为我知道问题的使用String.equals()是在字符串池上的引用

Iva*_*que 7

"当串联创建字符串时,java会使某些东西变得不同或简单= =比较器有另一种行为吗?"

不,它不会改变它的行为,会发生什么:

当连接两个字符串文字时"a" + "b",jvm连接两个值,然后检查字符串池,然后它实现池中已存在的值,因此它只是简单地将此引用分配给String.现在更详细:

看看这个简单程序下面的编译字节码:

public class Test  {    
    public static void main(String... args) {
        String a = "hello world!";
        String b = "hello" + " world!";
        boolean compare = (a == b);
    }
}
Run Code Online (Sandbox Code Playgroud)

简单的程序

首先,JVM加载字符串"hello world!然后将其推送到字符串池(在本例中),然后将其加载到堆栈(ldc =加载常量)[参见Image中的第1点]

然后它将池中创建的引用分配给局部变量(astore_1)[参见Image中的第2点]

请注意,在字符串池中为此文字创建的引用是#2 [参见图像中的第3点]

下一个操作大致相同:在连接字符串时,将其推送到运行时常量池(在本例中为字符串池),但随后它会实现具有相同内容的文字已存在,因此它使用此引用(#2)和分配给局部变量(astore_2).

因此当你这样做时(a == b)是真的,因为它们都引用了字符串池#2,即"hello world!".

您的示例C有点不同,因为您使用的是+ =运算符,当编译为字节码时,它使用StringBuilder来连接字符串,因此这将创建一个StringBuilder Object的新实例,从而指向不同的引用.(字符串池vs对象)

  • 由于两条“ldc #2”指令都在*已编译的*代码中,因此*编译器*执行了字符串连接和常量折叠。JVM 看到两个“ldc”指令指向同一个池条目(“#2”)。它不必“意识到”这些常量是相同的,这是显而易见的。在运行时,不会发生字符串连接。JVM 甚至不知道该常量的第二次使用是编译时的字符串连接。 (2认同)
  • 关于“示例C”的答案是不正确的。首先: += 运算符在编译为字节码时不使用 StringBuilder 来连接字符串。它使用 StringConcatFactory.makeConcatWithConstants。第二:它返回 false,因为使用 += 运算符时,新创建的字符串不会添加到字符串池中。使用文字字符串符号(双引号文本)创建的字符串和标记为最终变量的字符串 - 添加到字符串池。连接的字符串在堆上创建为新对象。与字符串池中的字符串相比,它们持有不同的引用。 (2认同)