1) 这是因为 String 对象是不可变的。一旦你创建了一个 String 对象并给它赋值,它将永远拥有相同的值,无法更改。因此,当你对其进行任何操作(例如连接)时,总是会创建一个新的 String 对象。
在堆中,有一个专门用于 String 常量的池子。如果编译器遇到一个 String 文字,它首先查找这个内存部分,看看是否已经存在相同的对象 (String)。如果存在,则分配一个引用到这个已经存在的对象 (String),而不是创建一个新的对象。(这就是为什么 String 是不可变的原因,因此没有人可以更改它。因为如果几个引用指向这个 String,其中一个引用更改其值,就会造成混乱。)
现在让我们看看在这段代码片段中发生了什么,并尝试同时回答你的两个问题。
(1) String a="hello"+"world";
(2) String b="hello";
(3) String c="world";
(4) String d=b+c;
(5) String e="helloworld";
(6) System.out.println(e==a);
(7) System.out.println(a==d);
(1) 首先创建一个指向String类型的引用值a。然后在先前提到的堆中的特殊池中创建了文字"hello"(第一个String对象)。接着在池中创建了文字"world"(第二个String对象)。由于字符串是不可变的,因此在这个池中创建了另一个文字"helloworld",并将其分配给引用变量a(第三个String对象-这是您第二个问题的答案)。
(2) 在(1)中创建的文字"hello"和"world"都是在池中创建的,并没有分配给任何引用值,但它们仍然存在于该池中。在这一行中,创建了String类型的引用变量b,并将其分配给池中现有的文字"hello"。
(3) 与(2)相同。
(4) 创建了类型为String的引用值d。虽然在这个特殊的堆部分(池)中已经有一个值为"helloworld"的String字面量,但是在堆中创建了一个具有值"helloworld"的新String对象,即在非池堆的部分。因此,您有两个不同类型为String且值为"helloworld"的对象(一个在池中,另一个在非池堆的部分)。如果现在使用==操作符比较引用值a和d,则它们都指向不同的对象(尽管值相同,因此equals方法将返回true)。
(5) 创建了类型为String的引用值e。在堆的池部分中已经有一个String "helloworld"(我们在(1)中创建了它并将其指向引用变量a),因此将引用变量e分配给此对象。并且指向与a相同的位置。因此,如果比较引用a == e,则为true。对于equals方法也是如此。
(6)和(7)在前面的点中已经解释过了
简而言之:
1)因为它不是同一个对象
2) 3