Java:字符串字面值和+运算符

7
import java.util.*;
import java.lang.*;
import java.io.*;    

class Test {
   public static void main (String[] args)  {

      String a="hello"+"world";  //line 1
      String b="hello";
      String c="world";
      String d=b+c;
      String e="helloworld";
      System.out.println(e==a);
      System.out.println(a==d);
     }
}

输出:

true

false

从这个讨论中(String类如何重写+运算符?),我了解到'a'和'e'将引用同一个字符串字面量对象。

请问:

  1. 为什么ad没有引用同一个字符串字面量对象?

  2. 第1行创建了多少个对象?

5个回答

14

来自JLS 4.3.3. 类String

字符串连接运算符+(§15.18.1)在结果不是编译时常量表达式(§15.28)时隐式创建一个新的String对象。

因为a的文字值是"helloworld",d的文字值是b + c 的引用对象,而该对象不是字面量,正如上述JLS 4.3.3所述。

来自JLS 3.10.5 字符串字面量

字符串字面量由双引号括起来的零个或多个字符组成。

来自15.28. 常量表达式

编译时常量表达式是指一个表示原始类型或字符串值的表达式,它不会突然终止,并且仅使用以下内容组成:

  • 原始类型字面量和字符串字面量(§3.10.1、§3.10.2、§3.10.3、§3.10.4、§3.10.5)

如果您想使 System.out.println(a==d); true,请使用 final 关键字,参见下面的代码:

  String a="hello"+"world";  //line 1
  final String b="hello"; // now b is a literal
  final String c="world";// now c is a literal
  String d=b+c;
  String e="helloworld";
  System.out.println(e==a);
  System.out.println(a==d);// now it will be true because both are literals.

评论回答:
第一行创建了多少个对象:答案是3
String a="hello"+"world";

第1个文字对象 hello
第2个文字对象 world
第3个文字对象 "hello"+"world" == "helloworld"


@SumitSingh 谢谢。为了获得更多澄清 -> 第1行的内部表示是new StringBuilder("hello").append("world").toString()吗? - nantitv
从您最新的回答更新中,我猜测第一行的内部表示不是“new StringBuilder("hello").append("world").toString()”。 - nantitv

2

==符号检查变量的引用是否相同。要检查任何对象是否等于其他对象,请使用Object.equals(Object o)方法,如下所示:

e.equals(a);

1

1) 这是因为 String 对象是不可变的。一旦你创建了一个 String 对象并给它赋值,它将永远拥有相同的值,无法更改。因此,当你对其进行任何操作(例如连接)时,总是会创建一个新的 String 对象。

在堆中,有一个专门用于 String 常量的池子。如果编译器遇到一个 String 文字,它首先查找这个内存部分,看看是否已经存在相同的对象 (String)。如果存在,则分配一个引用到这个已经存在的对象 (String),而不是创建一个新的对象。(这就是为什么 String 是不可变的原因,因此没有人可以更改它。因为如果几个引用指向这个 String,其中一个引用更改其值,就会造成混乱。)

现在让我们看看在这段代码片段中发生了什么,并尝试同时回答你的两个问题。

(1) String a="hello"+"world";  //line 1
(2) String b="hello"; // now b is a literal
(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


1
< p > == 运算符检查引用相等性,而不是对象相等性。在您的示例中,创建了两个内容为"helloworld"String实例。变量ae引用同一实例(见下文),而ad则不是。可以使用equals()方法检查字符串相等性,即a.equals(d)为true。

每当遇到字符串常量(如“helloworld”)时,都会检查是否已经遇到过这样的常量,如果已经有了,则返回旧的String实例而不是创建一个新的实例。尽管“hello” + “world”看起来不像一个字符串常量,但它被Java编译器优化为一个字符串常量,并且因此像e一样被interned,这就是为什么a == e是true的原因。相反,b + c没有被优化为常量,这就是为什么a == d是false的原因(然而,a.equals(d)将是true)。

0

== 操作符检查两个对象是否引用了内存中的同一位置。

a = "hello" + "world;" 的计算结果为字面值"helloworld",并给出了e="helloworld"。它们引用同一内存位置。因此表达式e == a的结果为true。

表达式d=b+c的计算结果也为"helloworld",但此结果存储在不同的位置。它不引用同一对象。因此表达式a == d的结果为false。

使用a.equals(d)将输出true,因为equals()检查两个对象的内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接